diff --git a/.gitignore b/.gitignore index 93194fc1b..58abdcf73 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ ocamlz3 # Emacs temp files \#*\# # Directories with generated code and documentation +release/* build/* build-dist/* dist/* diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index 1b498fb19..e45f56134 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft Corporation 2015 + import os import shutil import re diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 9f4f9431c..660db61d8 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include #include"z3++.h" @@ -205,6 +211,9 @@ void bitvector_example1() { // using unsigned <= prove(ule(x - 10, 0) == ule(x, 10)); + + expr y = c.bv_const("y", 32); + prove(implies(concat(x, y) == concat(y, x), x == y)); } /** @@ -977,6 +986,30 @@ void substitute_example() { std::cout << new_f << std::endl; } +void opt_example() { + context c; + optimize opt(c); + params p(c); + p.set("priority",c.str_symbol("pareto")); + opt.set(p); + expr x = c.int_const("x"); + expr y = c.int_const("y"); + opt.add(10 >= x && x >= 0); + opt.add(10 >= y && y >= 0); + opt.add(x + y <= 11); + optimize::handle h1 = opt.maximize(x); + optimize::handle h2 = opt.maximize(y); + check_result r = sat; + while (true) { + if (sat == opt.check()) { + std::cout << x << ": " << opt.lower(h1) << " " << y << ": " << opt.lower(h2) << "\n"; + } + else { + break; + } + } +} + void extract_example() { std::cout << "extract example\n"; context c; @@ -1025,6 +1058,7 @@ int main() { expr_vector_example(); std::cout << "\n"; exists_expr_vector_example(); std::cout << "\n"; substitute_example(); std::cout << "\n"; + opt_example(); std::cout << "\n"; extract_example(); std::cout << "\n"; std::cout << "done\n"; } diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index c0ea70453..cb63fe623 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include #include #include diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index e1b5fb6d8..f3335d80f 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -759,7 +759,7 @@ namespace test_mapi foreach (BoolExpr a in g.Formulas) solver.Assert(a); - if (solver.Check() != Status.SATISFIABLE) + if (solver.Check() != Status.SATISFIABLE) throw new TestFailedException(); ApplyResult ar = ApplyTactic(ctx, ctx.MkTactic("simplify"), g); @@ -2159,6 +2159,7 @@ namespace test_mapi Console.Write("Z3 Full Version: "); Console.WriteLine(Microsoft.Z3.Version.ToString()); + SimpleExample(); // These examples need model generation turned on. diff --git a/examples/interp/iz3.cpp b/examples/interp/iz3.cpp index 6d631ebc9..9f55c1ec6 100755 --- a/examples/interp/iz3.cpp +++ b/examples/interp/iz3.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include #include #include diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 4a579f9da..e27bab3bf 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -2254,7 +2254,30 @@ class JavaExample System.out.println("OK, model: " + s.getModel().toString()); } - + + public void optimizeExample(Context ctx) + { + System.out.println("Opt"); + + Optimize opt = ctx.mkOptimize(); + + // Set constraints. + IntExpr xExp = ctx.mkIntConst("x"); + IntExpr yExp = ctx.mkIntConst("y"); + + opt.Add(ctx.mkEq(ctx.mkAdd(xExp, yExp), ctx.mkInt(10)), + ctx.mkGe(xExp, ctx.mkInt(0)), + ctx.mkGe(yExp, ctx.mkInt(0))); + + // Set objectives. + Optimize.Handle mx = opt.MkMaximize(xExp); + Optimize.Handle my = opt.MkMaximize(yExp); + + System.out.println(opt.Check()); + System.out.println(mx); + System.out.println(my); + } + public static void main(String[] args) { JavaExample p = new JavaExample(); @@ -2274,6 +2297,8 @@ class JavaExample HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); + + p.optimizeExample(ctx); p.basicTests(ctx); p.castingTest(ctx); p.sudokuExample(ctx); diff --git a/examples/maxsat/maxsat.c b/examples/maxsat/maxsat.c index 5ca707811..f04c25e2c 100644 --- a/examples/maxsat/maxsat.c +++ b/examples/maxsat/maxsat.c @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + /* Simple MAXSAT solver on top of the Z3 API. */ diff --git a/examples/msf/README b/examples/msf/README new file mode 100644 index 000000000..d6e56f72f --- /dev/null +++ b/examples/msf/README @@ -0,0 +1,20 @@ +In order to use Z3 MSF plugin, follow the following steps: +1. Compile latest Z3 .NET API (from any branch consisting of opt features) and copy 'libz3.dll' and 'Microsoft.Z3.dll' to the folder of 'Z3MSFPlugin.sln'. +2. Retrieve 'Microsoft.Solver.Foundation.dll' from http://archive.msdn.microsoft.com/solverfoundation/Release/ProjectReleases.aspx?ReleaseId=1799, + preferably using DLL only version. Copy 'Microsoft.Solver.Foundation.dll' to the folder of 'Z3MSFPlugin.sln' +3. Build 'Z3MSFPlugin.sln'. Note that you have to compile using x86 target for Microsoft.Z3.dll 32-bit and x64 target for Microsoft.Z3.dll 64-bit. + +The solution consists of a plugin project, a test project with a few simple test cases and a command line projects for external OML, MPS and SMPS models. +To retrieve SMT2 models which are native to Z3, set the logging file in corresponding directives or solver params e.g. + + var p = new Z3MILPParams(); + p.SMT2LogFile = "model.smt2"; + +For more details, check out the commands in 'Validator\Program.cs'. + +Enjoy! + + + + + \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config b/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config new file mode 100644 index 000000000..75e2872f1 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3.Tests/App.config @@ -0,0 +1,60 @@ + + + +
+ + + + + + + + + + + + + + + + diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs b/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..b58f97eda --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SolverFoundation.Plugin.Z3.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SolverFoundation.Plugin.Z3.Tests")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("27657eee-ca7b-4996-a905-86a3f4584988")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs b/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs new file mode 100644 index 000000000..196f89245 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs @@ -0,0 +1,92 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Microsoft.SolverFoundation.Common; +using Microsoft.SolverFoundation.Solvers; +using Microsoft.SolverFoundation.Services; +using Microsoft.SolverFoundation.Plugin.Z3; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.SolverFoundation.Plugin.Z3.Tests +{ + [TestClass] + public class ServiceTests + { + [TestInitialize] + public void SetUp() + { + SolverContext context = SolverContext.GetContext(); + context.ClearModel(); + } + + private void TestService1(Directive directive) + { + SolverContext context = SolverContext.GetContext(); + Model model = context.CreateModel(); + + Decision x1 = new Decision(Domain.RealRange(0, 2), "x1"); + Decision x2 = new Decision(Domain.RealRange(0, 2), "x2"); + + Decision z = new Decision(Domain.IntegerRange(0, 1), "z"); + + model.AddDecisions(x1, x2, z); + + model.AddConstraint("Row0", x1 - z <= 1); + model.AddConstraint("Row1", x2 + z <= 2); + + Goal goal = model.AddGoal("Goal0", GoalKind.Maximize, x1 + x2); + + Solution solution = context.Solve(directive); + Assert.IsTrue(goal.ToInt32() == 3); + context.ClearModel(); + } + + private void TestService2(Directive directive) + { + SolverContext context = SolverContext.GetContext(); + Model model = context.CreateModel(); + + Decision x1 = new Decision(Domain.RealNonnegative, "x1"); + Decision x2 = new Decision(Domain.RealNonnegative, "x2"); + + Decision z = new Decision(Domain.IntegerRange(0, 1), "z"); + + Rational M = 100; + + model.AddDecisions(x1, x2, z); + + model.AddConstraint("Row0", x1 + x2 >= 1); + model.AddConstraint("Row1", x1 - z * 100 <= 0); + model.AddConstraint("Row2", x2 + z * 100 <= 100); + + Goal goal = model.AddGoal("Goal0", GoalKind.Maximize, x1 + x2); + + Solution solution = context.Solve(directive); + Assert.IsTrue(goal.ToInt32() == 100); + context.ClearModel(); + } + + [TestMethod] + public void TestService1() + { + TestService1(new Z3MILPDirective()); + TestService1(new Z3TermDirective()); + } + + [TestMethod] + public void TestService2() + { + TestService2(new Z3MILPDirective()); + TestService2(new Z3TermDirective()); + } + + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj b/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj new file mode 100644 index 000000000..24cecfa10 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverFoundation.Plugin.Z3.Tests.csproj @@ -0,0 +1,70 @@ + + + + + Debug + AnyCPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B} + Library + Properties + Microsoft.SolverFoundation.Plugin.Z3.Tests + SolverFoundation.Plugin.Z3.Tests + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x86 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x86 + + + + ..\Microsoft.Solver.Foundation.dll + + + + + + + + + + + + + + + + + + + + + {7340e664-f648-4ff7-89b2-f4da424996d3} + SolverFoundation.Plugin.Z3 + + + + + \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs b/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs new file mode 100644 index 000000000..4913c0f81 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs @@ -0,0 +1,138 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Microsoft.SolverFoundation.Common; +using Microsoft.SolverFoundation.Solvers; +using Microsoft.SolverFoundation.Services; +using Microsoft.SolverFoundation.Plugin.Z3; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.SolverFoundation.Plugin.Z3.Tests +{ + [TestClass] + public class SolverTests + { + [TestMethod] + public void TestMILPSolver1() + { + var solver = new Z3MILPSolver(); + int goal; + + solver.AddRow("goal", out goal); + int x1, x2, z; + + // 0 <= x1 <= 2 + solver.AddVariable("x1", out x1); + solver.SetBounds(x1, 0, 2); + + // 0 <= x2 <= 2 + solver.AddVariable("x2", out x2); + solver.SetBounds(x2, 0, 2); + + // z is an integer in [0,1] + solver.AddVariable("z", out z); + solver.SetIntegrality(z, true); + solver.SetBounds(z, 0, 1); + + //max x1 + x2 + solver.SetCoefficient(goal, x1, 1); + solver.SetCoefficient(goal, x2, 1); + solver.AddGoal(goal, 1, false); + + // 0 <= x1 -z <= 1 + int row1; + solver.AddRow("rowI1", out row1); + solver.SetBounds(row1, 0, 1); + solver.SetCoefficient(row1, x1, 1); + solver.SetCoefficient(row1, z, -1); + + // 0 <= x2 + z <= 2 + int row2; + solver.AddRow("rowI2", out row2); + solver.SetBounds(row2, 0, 2); + solver.SetCoefficient(row2, x2, 1); + solver.SetCoefficient(row2, z, 1); + + var p = new Z3MILPParams(); + solver.Solve(p); + + Assert.IsTrue(solver.Result == LinearResult.Optimal); + Assert.AreEqual(solver.GetValue(x1), 2 * Rational.One); + Assert.AreEqual(solver.GetValue(x2), Rational.One); + Assert.AreEqual(solver.GetValue(z), Rational.One); + Assert.AreEqual(solver.GetValue(goal), 3 * Rational.One); + } + + [TestMethod] + public void TestMILPSolver2() + { + var solver = new Z3MILPSolver(); + int goal, extraGoal; + + Rational M = 100; + solver.AddRow("goal", out goal); + int x1, x2, z; + + // 0 <= x1 <= 100 + solver.AddVariable("x1", out x1); + solver.SetBounds(x1, 0, M); + + // 0 <= x2 <= 100 + solver.AddVariable("x2", out x2); + solver.SetBounds(x2, 0, M); + + // z is an integer in [0,1] + solver.AddVariable("z", out z); + solver.SetIntegrality(z, true); + solver.SetBounds(z, 0, 1); + + solver.SetCoefficient(goal, x1, 1); + solver.SetCoefficient(goal, x2, 2); + solver.AddGoal(goal, 1, false); + + solver.AddRow("extraGoal", out extraGoal); + + solver.SetCoefficient(extraGoal, x1, 2); + solver.SetCoefficient(extraGoal, x2, 1); + solver.AddGoal(extraGoal, 2, false); + + // x1 + x2 >= 1 + int row; + solver.AddRow("row", out row); + solver.SetBounds(row, 1, Rational.PositiveInfinity); + solver.SetCoefficient(row, x1, 1); + solver.SetCoefficient(row, x2, 1); + + + // x1 - M*z <= 0 + int row1; + solver.AddRow("rowI1", out row1); + solver.SetBounds(row1, Rational.NegativeInfinity, 0); + solver.SetCoefficient(row1, x1, 1); + solver.SetCoefficient(row1, z, -M); + + // x2 - M* (1-z) <= 0 + int row2; + solver.AddRow("rowI2", out row2); + solver.SetBounds(row2, Rational.NegativeInfinity, M); + solver.SetCoefficient(row2, x2, 1); + solver.SetCoefficient(row2, z, M); + + var p = new Z3MILPParams(); + p.OptKind = OptimizationKind.BoundingBox; + + solver.Solve(p); + Assert.IsTrue(solver.Result == LinearResult.Optimal); + Assert.AreEqual(solver.GetValue(goal), 200 * Rational.One); + Assert.AreEqual(solver.GetValue(extraGoal), 200 * Rational.One); + } + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs b/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs new file mode 100644 index 000000000..6ce66fa8f --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs @@ -0,0 +1,98 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Microsoft.Z3; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + /// + /// Thread that will wait until the query abort function returns true or + /// the stop method is called. If the abort function returns true at some + /// point it will issue a softCancel() call to Z3. + /// + internal class AbortWorker + { + #region Private Members + + /// The Z3 solver + private Microsoft.Z3.Context _context; + /// The abort function to use to check if we are aborted + private Func _QueryAbortFunction; + /// Flag indicating that worker should stop + private bool _stop = false; + /// Flag indicating that we have been sent an abort signal + private bool _aborted = false; + + #endregion Private Members + + #region Construction + + /// + /// Worker constructor taking a Z3 instance and a function to periodically + /// check for aborts. + /// + /// Z3 instance + /// method to call to check for aborts + public AbortWorker(Context context, Func queryAbortFunction) + { + _context = context; + _QueryAbortFunction = queryAbortFunction; + } + + #endregion Construction + + #region Public Methods + + /// + /// Stop the abort worker. + /// + public void Stop() + { + _stop = true; + } + + /// + /// Is true if we have been aborted. + /// + public bool Aborted + { + get + { + return _aborted; + } + } + + /// + /// Starts the abort worker. The worker checks the abort method + /// periodically until either it is stopped by a call to the Stop() + /// method or it gets an abort signal. In the latter case it will + /// issue a soft abort signal to Z3. + /// + public void Start() + { + // We go until someone stops us + _stop = false; + while (!_stop && !_QueryAbortFunction()) + { + // Wait for a while + Thread.Sleep(10); + } + // If we were stopped on abort, cancel z3 + if (!_stop) + { + _context.Interrupt(); + _aborted = true; + } + } + + #endregion Public Methods + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/App.config b/examples/msf/SolverFoundation.Plugin.Z3/App.config new file mode 100644 index 000000000..75e2872f1 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/App.config @@ -0,0 +1,60 @@ + + + +
+ + + + + + + + + + + + + + + + diff --git a/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj b/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj new file mode 100644 index 000000000..0b30e1677 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/SolverFoundation.Plugin.Z3.csproj @@ -0,0 +1,149 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {7340E664-F648-4FF7-89B2-F4DA424996D3} + Library + Properties + Microsoft.SolverFoundation.Plugin.Z3 + SolverFoundation.Plugin.Z3 + v4.0 + 512 + false + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + bin\commercial\ + TRACE + true + pdbonly + AnyCPU + prompt + + + bin\commercial_64\ + TRACE + true + pdbonly + AnyCPU + prompt + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + AllRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + AllRules.ruleset + + + bin\x86\commercial\ + TRACE + true + pdbonly + x86 + prompt + AllRules.ruleset + + + bin\x86\commercial_64\ + TRACE + true + pdbonly + x86 + prompt + + + + ..\Microsoft.Solver.Foundation.dll + + + False + ..\Microsoft.Z3.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs b/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs new file mode 100644 index 000000000..5930caee1 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs @@ -0,0 +1,130 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; +using Microsoft.Z3; +using Microsoft.SolverFoundation.Common; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + public class Utils + { + /// + /// Returns the Z3 term corresponding to the MSF rational number. + /// + /// The MSF rational + /// The Z3 term + public static ArithExpr GetNumeral(Context context, Rational rational, Sort sort = null) + { + try + { + sort = rational.IsInteger() ? ((Sort)context.IntSort) : (sort == null ? (Sort)context.RealSort : sort); + return (ArithExpr)context.MkNumeral(rational.ToString(), sort); + } + catch (Z3Exception e) + { + Console.Error.WriteLine("Conversion of {0} failed:\n {1}", rational, e); + throw new NotSupportedException(); + } + } + + private static long BASE = 10 ^ 18; + + private static Rational ToRational(System.Numerics.BigInteger bi) + { + if (System.Numerics.BigInteger.Abs(bi) <= BASE) + { + return (Rational)((long)bi); + } + return BASE * ToRational(bi / BASE) + ToRational(bi % BASE); + } + + public static Rational ToRational(IntNum i) + { + return ToRational(i.BigInteger); + } + + public static Rational ToRational(RatNum r) + { + return ToRational(r.BigIntNumerator) / ToRational(r.BigIntDenominator); + } + + public static Rational ToRational(Expr expr) + { + Debug.Assert(expr is ArithExpr, "Only accept ArithExpr for now."); + var e = expr as ArithExpr; + + if (e is IntNum) + { + Debug.Assert(expr.IsIntNum, "Number should be an integer."); + return ToRational(expr as IntNum); + } + + if (e is RatNum) + { + Debug.Assert(expr.IsRatNum, "Number should be a rational."); + return ToRational(expr as RatNum); + } + + if (e.IsAdd) + { + Rational r = Rational.Zero; + foreach (var arg in e.Args) + { + r += ToRational(arg); + } + return r; + } + + if (e.IsMul) + { + Rational r = Rational.One; + foreach (var arg in e.Args) + { + r *= ToRational(arg); + } + return r; + } + + if (e.IsUMinus) + { + return -ToRational(e.Args[0]); + } + + if (e.IsDiv) + { + return ToRational(e.Args[0]) / ToRational(e.Args[1]); + } + + if (e.IsSub) + { + Rational r = ToRational(e.Args[0]); + for (int i = 1; i < e.Args.Length; ++i) + { + r -= ToRational(e.Args[i]); + } + return r; + } + + if (e.IsConst && e.FuncDecl.Name.ToString() == "oo") + { + return Rational.PositiveInfinity; + } + + if (e.IsConst && e.FuncDecl.Name.ToString() == "epsilon") + { + return Rational.One/Rational.PositiveInfinity; + } + + Debug.Assert(false, "Should not happen"); + return Rational.One; + } + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs new file mode 100644 index 000000000..199c3fe35 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs @@ -0,0 +1,107 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.Text; +using Microsoft.SolverFoundation.Services; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + /// + /// Combining objective functions + /// + public enum OptimizationKind + { + Lexicographic, + BoundingBox, + ParetoOptimal + }; + + /// + /// Algorithm for solving cardinality constraints + /// + public enum CardinalityAlgorithm + { + FuMalik, + CoreMaxSAT + } + + /// + /// Algorithm for solving pseudo-boolean constraints + /// + public enum PseudoBooleanAlgorithm + { + WeightedMaxSAT, + IterativeWeightedMaxSAT, + BisectionWeightedMaxSAT, + WeightedPartialMaxSAT2 + } + + /// + /// Strategy for solving arithmetic optimization + /// + public enum ArithmeticStrategy + { + Basic, + Farkas + } + + public abstract class Z3BaseDirective : Directive + { + protected OptimizationKind _optKind; + protected CardinalityAlgorithm _cardAlgorithm; + protected PseudoBooleanAlgorithm _pboAlgorithm; + protected ArithmeticStrategy _arithStrategy; + + protected string _smt2LogFile; + + public Z3BaseDirective() + { + Arithmetic = Arithmetic.Exact; + } + + public OptimizationKind OptKind + { + get { return _optKind; } + set { _optKind = value; } + } + + public CardinalityAlgorithm CardinalityAlgorithm + { + get { return _cardAlgorithm; } + set { _cardAlgorithm = value; } + } + + public PseudoBooleanAlgorithm PseudoBooleanAlgorithm + { + get { return _pboAlgorithm; } + set { _pboAlgorithm = value; } + } + + public ArithmeticStrategy ArithmeticStrategy + { + get { return _arithStrategy; } + set { _arithStrategy = value; } + } + + public string SMT2LogFile + { + get { return _smt2LogFile; } + set { _smt2LogFile = value; } + } + + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append(this.GetType().Name); + sb.Append("("); + sb.AppendFormat("OptKind: {0}, ", _optKind); + sb.AppendFormat("SMT2LogFile: {0}", _smt2LogFile); + sb.Append(")"); + return sb.ToString(); + } + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs new file mode 100644 index 000000000..6d6dd74a7 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs @@ -0,0 +1,109 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using Microsoft.SolverFoundation.Services; +using System; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + /// + /// Implementation of the solver parameters for Z3 + /// + public class Z3BaseParams : ISolverParameters + { + #region Private Members + + /// The abort method we can call (defaults to always false) + protected Func _queryAbortFunction = delegate() { return false; }; + + /// The directive to use + protected Directive _directive = null; + + protected OptimizationKind _optKind; + protected CardinalityAlgorithm _cardAlgorithm; + protected PseudoBooleanAlgorithm _pboAlgorithm; + protected ArithmeticStrategy _arithStrategy; + + protected string _smt2LogFile; + + #endregion Private Members + + #region Construction + + public Z3BaseParams() { } + + public Z3BaseParams(Directive directive) + { + _directive = directive; + + var z3Directive = directive as Z3BaseDirective; + if (z3Directive != null) + { + _optKind = z3Directive.OptKind; + _cardAlgorithm = z3Directive.CardinalityAlgorithm; + _pboAlgorithm = z3Directive.PseudoBooleanAlgorithm; + _arithStrategy = z3Directive.ArithmeticStrategy; + _smt2LogFile = z3Directive.SMT2LogFile; + } + } + + public Z3BaseParams(Func queryAbortFunction) + { + _queryAbortFunction = queryAbortFunction; + } + + public Z3BaseParams(Z3BaseParams z3Parameters) + { + _queryAbortFunction = z3Parameters._queryAbortFunction; + } + + #endregion Construction + + #region ISolverParameters Members + + /// + /// Getter for the abort method + /// + public Func QueryAbort + { + get { return _queryAbortFunction; } + set { _queryAbortFunction = value; } + } + + public OptimizationKind OptKind + { + get { return _optKind; } + set { _optKind = value; } + } + + public CardinalityAlgorithm CardinalityAlgorithm + { + get { return _cardAlgorithm; } + set { _cardAlgorithm = value; } + } + + public PseudoBooleanAlgorithm PseudoBooleanAlgorithm + { + get { return _pboAlgorithm; } + set { _pboAlgorithm = value; } + } + + public ArithmeticStrategy ArithmeticStrategy + { + get { return _arithStrategy; } + set { _arithStrategy = value; } + } + + public string SMT2LogFile + { + get { return _smt2LogFile; } + set { _smt2LogFile = value; } + } + + #endregion + } + +} \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs new file mode 100644 index 000000000..af8bc92a2 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs @@ -0,0 +1,387 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.Collections.Generic; +using System.Threading; +using System.IO; +using System.Linq; +using System.Text; +using System.Diagnostics; +using Microsoft.Z3; +using Microsoft.SolverFoundation.Common; +using Microsoft.SolverFoundation.Services; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + internal enum Z3Result + { + Optimal, + LocalOptimal, + Feasible, + Interrupted, + Infeasible + } + + /// + /// The basic solver class to take care of transformation from an MSF instance to an Z3 instance + /// + internal class Z3BaseSolver + { + /// Representing MSF model + private IRowVariableModel _model; + + /// The Z3 solver we are currently using + private Context _context = null; + + /// Default optimization solver + private Optimize _optSolver = null; + + /// Marks when we are inside the Solve() method + private bool _isSolving = false; + + /// A map from MSF variable ids to Z3 variables + private Dictionary _variables = new Dictionary(); + + /// A map from MSF variable ids to Z3 goal ids + private Dictionary _goals = new Dictionary(); + + internal Z3BaseSolver(IRowVariableModel model) + { + _model = model; + } + + internal Context Context + { + get { return _context; } + } + + internal Dictionary Variables + { + get { return _variables; } + } + + internal Dictionary Goals + { + get { return _goals; } + } + + /// + /// Destructs a currently active Z3 solver and the associated data. + /// + internal void DestructSolver(bool checkInSolve) + { + if (_context != null) + { + if (checkInSolve && !_isSolving) + { + _variables.Clear(); + if (!_isSolving) + { + _optSolver.Dispose(); + _context.Dispose(); + } + } + else + { + Console.Error.WriteLine("Z3 destruction is invoked while in Solving phase."); + } + } + } + + /// + /// Constructs a Z3 solver to be used. + /// + internal void ConstructSolver(Z3BaseParams parameters) + { + // If Z3 is there already, kill it + if (_context != null) + { + DestructSolver(false); + } + + _context = new Context(); + _optSolver = _context.MkOptimize(); + var p = _context.MkParams(); + + switch (parameters.OptKind) + { + case OptimizationKind.BoundingBox: + p.Add("priority", _context.MkSymbol("box")); + break; + case OptimizationKind.Lexicographic: + p.Add("priority", _context.MkSymbol("lex")); + break; + case OptimizationKind.ParetoOptimal: + p.Add("priority", _context.MkSymbol("pareto")); + break; + default: + Debug.Assert(false, String.Format("Unknown optimization option {0}", parameters.OptKind)); + break; + } + + switch (parameters.CardinalityAlgorithm) + { + case CardinalityAlgorithm.FuMalik: + p.Add("maxsat_engine", _context.MkSymbol("fu_malik")); + break; + case CardinalityAlgorithm.CoreMaxSAT: + p.Add("maxsat_engine", _context.MkSymbol("core_maxsat")); + break; + default: + Debug.Assert(false, String.Format("Unknown cardinality algorithm option {0}", parameters.CardinalityAlgorithm)); + break; + } + + switch (parameters.PseudoBooleanAlgorithm) + { + case PseudoBooleanAlgorithm.WeightedMaxSAT: + p.Add("wmaxsat_engine", _context.MkSymbol("wmax")); + break; + case PseudoBooleanAlgorithm.IterativeWeightedMaxSAT: + p.Add("wmaxsat_engine", _context.MkSymbol("iwmax")); + break; + case PseudoBooleanAlgorithm.BisectionWeightedMaxSAT: + p.Add("wmaxsat_engine", _context.MkSymbol("bwmax")); + break; + case PseudoBooleanAlgorithm.WeightedPartialMaxSAT2: + p.Add("wmaxsat_engine", _context.MkSymbol("wpm2")); + break; + default: + Debug.Assert(false, String.Format("Unknown pseudo-boolean algorithm option {0}", parameters.PseudoBooleanAlgorithm)); + break; + } + + switch (parameters.ArithmeticStrategy) + { + case ArithmeticStrategy.Basic: + p.Add("engine", _context.MkSymbol("basic")); + break; + case ArithmeticStrategy.Farkas: + p.Add("engine", _context.MkSymbol("farkas")); + break; + default: + Debug.Assert(false, String.Format("Unknown arithmetic strategy option {0}", parameters.ArithmeticStrategy)); + break; + } + + _optSolver.Parameters = p; + } + + internal ArithExpr GetVariable(int vid) + { + Expr variable; + if (!_variables.TryGetValue(vid, out variable)) + { + AddVariable(vid); + variable = _variables[vid]; + } + return (ArithExpr)variable; + } + + internal void AssertBool(BoolExpr row) + { + _optSolver.Assert(row); + } + + internal void AssertArith(int vid, ArithExpr variable) + { + // Get the bounds on the row + Rational lower, upper; + _model.GetBounds(vid, out lower, out upper); + + // Case of equality + if (lower == upper) + { + // Create the equality term + Expr eqConst = GetNumeral(lower, variable.Sort); + BoolExpr constraint = _context.MkEq(eqConst, variable); + // Assert the constraint + _optSolver.Assert(constraint); + } + else + { + // If upper bound is finite assert the upper bound constraint + if (lower.IsFinite) + { + // Create the lower Bound constraint + ArithExpr lowerTerm = GetNumeral(lower, variable.Sort); + BoolExpr constraint = _context.MkLe(lowerTerm, variable); + // Assert the constraint + _optSolver.Assert(constraint); + } + // If lower bound is finite assert the lower bound constraint + if (upper.IsFinite) + { + // Create the upper bound constraint + ArithExpr upperTerm = GetNumeral(upper, variable.Sort); + BoolExpr constraint = _context.MkGe(upperTerm, variable); + // Assert the constraint + _optSolver.Assert(constraint); + } + } + } + + /// + /// Adds a MSF variable with the coresponding assertion to the Z3 variables. + /// + /// The MSF id of the variable + internal void AddVariable(int vid) + { + // Is the variable an integer + bool isInteger = _model.GetIntegrality(vid); + + // Construct the sort we will be using + Sort sort = isInteger ? (Sort)_context.IntSort : (Sort)_context.RealSort; + + // Get the variable key + object key = _model.GetKeyFromIndex(vid); + + // Try to construct the name + string name; + if (key != null) name = String.Format("x_{0}_{1}", key, vid); + else name = String.Format("x_{0}", vid); + ArithExpr variable = (ArithExpr)_context.MkConst(name, sort); + + // Create the variable and add it to the map + Debug.Assert(!_variables.ContainsKey(vid), "Variable names should be unique."); + _variables.Add(vid, variable); + + AssertArith(vid, variable); + } + + internal ArithExpr GetNumeral(Rational rational, Sort sort = null) + { + return Utils.GetNumeral(_context, rational, sort); + } + + internal void Solve(Z3BaseParams parameters, IEnumerable modelGoals, + Action addRow, Func mkGoalRow, Action setResult) + { + _variables.Clear(); + _goals.Clear(); + + try + { + // Mark that we are in solving phase + _isSolving = true; + + // Construct Z3 + ConstructSolver(parameters); + + // Add all the variables + foreach (int vid in _model.VariableIndices) + { + AddVariable(vid); + } + + // Add all the rows + foreach (int rid in _model.RowIndices) + { + addRow(rid); + } + + // Add enabled goals to optimization problem + foreach (IGoal g in modelGoals) + { + if (!g.Enabled) continue; + + ArithExpr gr = mkGoalRow(g.Index); + if (g.Minimize) + _goals.Add(g, _optSolver.MkMinimize(gr)); + else + _goals.Add(g, _optSolver.MkMaximize(gr)); + } + + if (_goals.Any() && parameters.SMT2LogFile != null) + { + Debug.WriteLine("Dumping SMT2 benchmark to log file..."); + File.WriteAllText(parameters.SMT2LogFile, _optSolver.ToString()); + } + + bool aborted = parameters.QueryAbort(); + + if (!aborted) + { + // Start the abort thread + AbortWorker abortWorker = new AbortWorker(_context, parameters.QueryAbort); + Thread abortThread = new Thread(abortWorker.Start); + abortThread.Start(); + + // Now solve the problem + Status status = _optSolver.Check(); + + // Stop the abort thread + abortWorker.Stop(); + abortThread.Join(); + + switch (status) + { + case Status.SATISFIABLE: + Microsoft.Z3.Model model = _optSolver.Model; + Debug.Assert(model != null, "Should be able to get Z3 model."); + // Remember the solution values + foreach (KeyValuePair pair in _variables) + { + var value = Utils.ToRational(model.Eval(pair.Value, true)); + _model.SetValue(pair.Key, value); + } + // Remember all objective values + foreach (var pair in _goals) + { + var optimalValue = Utils.ToRational(_optSolver.GetUpper(pair.Value)); + _model.SetValue(pair.Key.Index, optimalValue); + } + model.Dispose(); + setResult(_goals.Any() ? Z3Result.Optimal : Z3Result.Feasible); + break; + case Status.UNSATISFIABLE: + setResult(Z3Result.Infeasible); + break; + case Status.UNKNOWN: + if (abortWorker.Aborted) + { + Microsoft.Z3.Model subOptimalModel = _optSolver.Model; + if (subOptimalModel != null && subOptimalModel.NumConsts != 0) + { + // Remember the solution values + foreach (KeyValuePair pair in _variables) + { + var value = Utils.ToRational(subOptimalModel.Eval(pair.Value, true)); + _model.SetValue(pair.Key, value); + } + // Remember all objective values + foreach (var pair in _goals) + { + var optimalValue = Utils.ToRational(_optSolver.GetUpper(pair.Value)); + _model.SetValue(pair.Key.Index, optimalValue); + } + subOptimalModel.Dispose(); + + setResult(Z3Result.LocalOptimal); + } + else + setResult(Z3Result.Infeasible); + } + else + setResult(Z3Result.Interrupted); + break; + default: + Debug.Assert(false, "Unrecognized Z3 Status"); + break; + } + } + } + finally + { + _isSolving = false; + } + + // Now kill Z3 + DestructSolver(true); + } + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs new file mode 100644 index 000000000..4d6745634 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs @@ -0,0 +1,15 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using Microsoft.SolverFoundation.Services; +using System; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + public class Z3MILPDirective : Z3BaseDirective + { + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs new file mode 100644 index 000000000..d01b07725 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs @@ -0,0 +1,25 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using Microsoft.SolverFoundation.Services; +using System; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + public class Z3MILPParams : Z3BaseParams + { + // Need these constructors for reflection done by plugin model + + public Z3MILPParams() : base() { } + + public Z3MILPParams(Directive directive) : base(directive) { } + + public Z3MILPParams(Func queryAbortFunction) : base(queryAbortFunction) { } + + public Z3MILPParams(Z3BaseParams z3Parameters) : base (z3Parameters) { } + } + +} \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs new file mode 100644 index 000000000..f3a8f9f2c --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs @@ -0,0 +1,236 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.IO; + +using Microsoft.Z3; +using Microsoft.SolverFoundation.Common; +using Microsoft.SolverFoundation.Services; +using Microsoft.SolverFoundation.Plugin; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + /// + /// The class is implementation of the MSF mixed linear programming solver + /// using the Microsoft Z3 solver as the backend. + /// + public class Z3MILPSolver : LinearModel, ILinearSolver, ILinearSolution, IReportProvider + { + #region Private members + + private LinearResult _result; + private LinearSolutionQuality _solutionQuality; + private Z3BaseSolver _solver; + + #endregion Private members + + #region Solver construction and destruction + + /// Constructor that initializes the base clases + public Z3MILPSolver() : base(null) + { + _result = LinearResult.Feasible; + _solver = new Z3BaseSolver(this); + } + + /// Constructor that initializes the base clases + public Z3MILPSolver(ISolverEnvironment context) : this() { } + + /// + /// Shutdown can be called when when the solver is not active, i.e. + /// when it is done with Solve() or it has gracefully returns from Solve() + /// after an abort. + /// + public void Shutdown() { _solver.DestructSolver(true); } + + #endregion Solver construction and destruction + + #region Obtaining information about the solution + + public ILinearSolverReport GetReport(LinearSolverReportType reportType) + { + // We don't support sensitivity report + return null; + } + + #endregion Obtaining information about the solution + + #region Construction of the problem + + /// + /// Get corresponding Z3 formula of a MSF row. + /// + /// The MSF row id + private ArithExpr MkGoalRow(int rid) + { + // Start with the 0 term + List row = new List(); + + // Now, add all the entries of this row + foreach (LinearEntry entry in GetRowEntries(rid)) + { + // Get the variable and constant in the row + ArithExpr e = _solver.GetVariable(entry.Index); + if (!entry.Value.IsOne) + { + e = _solver.Context.MkMul(_solver.GetNumeral(entry.Value, e.Sort), e); + } + row.Add(e); + } + switch (row.Count) + { + case 0: return _solver.GetNumeral(new Rational()); + case 1: return row[0]; + default: return _solver.Context.MkAdd(row.ToArray()); + } + } + + /// + /// Adds a MSF row to the Z3 assertions. + /// + /// The MSF row id + private void AddRow(int rid) + { + // Start with the 0 term + ArithExpr row = MkGoalRow(rid); + _solver.AssertArith(rid, row); + } + + /// + /// Set results based on internal solver status + /// + private void SetResult(Z3Result status) + { + switch (status) + { + case Z3Result.Optimal: + _result = LinearResult.Optimal; + _solutionQuality = LinearSolutionQuality.Exact; + break; + case Z3Result.LocalOptimal: + _result = LinearResult.Feasible; + _solutionQuality = LinearSolutionQuality.Approximate; + break; + case Z3Result.Feasible: + _result = LinearResult.Feasible; + _solutionQuality = LinearSolutionQuality.Exact; + break; + case Z3Result.Infeasible: + _result = LinearResult.InfeasiblePrimal; + _solutionQuality = LinearSolutionQuality.None; + break; + case Z3Result.Interrupted: + _result = LinearResult.Interrupted; + _solutionQuality = LinearSolutionQuality.None; + break; + default: + Debug.Assert(false, "Unrecognized Z3 Result"); + break; + } + } + + #endregion Construction of the problem + + #region Solving the problem + + /// + /// Starts solving the problem using the Z3 solver. + /// + /// Parameters to the solver + /// The solution to the problem + public ILinearSolution Solve(ISolverParameters parameters) + { + // Get the Z3 parameters + var z3Params = parameters as Z3BaseParams; + Debug.Assert(z3Params != null, "Parameters should be an instance of Z3BaseParams."); + + _solver.Solve(z3Params, Goals, AddRow, MkGoalRow, SetResult); + + return this; + } + + #endregion Solving the problem + + #region ILinearSolution Members + + public Rational GetSolutionValue(int goalIndex) + { + var goal = Goals.ElementAt(goalIndex); + Debug.Assert(goal != null, "Goal should be an element of the goal list."); + return GetValue(goal.Index); + } + + public void GetSolvedGoal(int goalIndex, out object key, out int vid, out bool minimize, out bool optimal) + { + var goal = Goals.ElementAt(goalIndex); + Debug.Assert(goal != null, "Goal should be an element of the goal list."); + key = goal.Key; + vid = goal.Index; + minimize = goal.Minimize; + optimal = _result == LinearResult.Optimal; + } + + // LpResult is LP relaxation assignment. + + public LinearResult LpResult + { + get { return _result; } + } + + public Rational MipBestBound + { + get + { + Debug.Assert(GoalCount > 0, "MipBestBound is only applicable for optimization instances."); + return GetSolutionValue(0); + } + } + + public LinearResult MipResult + { + get { return _result; } + } + + public LinearResult Result + { + get { return _result; } + } + + public LinearSolutionQuality SolutionQuality + { + get { return _solutionQuality; } + } + + public int SolvedGoalCount + { + get { return GoalCount; } + } + + #endregion + + public Report GetReport(SolverContext context, Solution solution, SolutionMapping solutionMapping) + { + LinearSolutionMapping lpSolutionMapping = solutionMapping as LinearSolutionMapping; + if (lpSolutionMapping == null && solutionMapping != null) + throw new ArgumentException("solutionMapping is not a LinearSolutionMapping", "solutionMapping"); + return new Z3LinearSolverReport(context, this, solution, lpSolutionMapping); + } + } + + /// + /// Class implementing the LinearReport. + /// + public class Z3LinearSolverReport : LinearReport + { + public Z3LinearSolverReport(SolverContext context, ISolver solver, Solution solution, LinearSolutionMapping solutionMapping) + : base(context, solver, solution, solutionMapping) { + } + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs new file mode 100644 index 000000000..ff9e4181a --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs @@ -0,0 +1,15 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using Microsoft.SolverFoundation.Services; +using System; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + public class Z3TermDirective : Z3BaseDirective + { + } +} diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs new file mode 100644 index 000000000..283bc9362 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs @@ -0,0 +1,23 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using Microsoft.SolverFoundation.Services; +using System; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + public class Z3TermParams : Z3BaseParams + { + public Z3TermParams() : base() { } + + public Z3TermParams(Directive directive) : base(directive) { } + + public Z3TermParams(Func queryAbortFunction) : base(queryAbortFunction) { } + + public Z3TermParams(Z3BaseParams z3Parameters) : base(z3Parameters) { } + } + +} \ No newline at end of file diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs new file mode 100644 index 000000000..530df3394 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs @@ -0,0 +1,388 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.Threading; +using System.Globalization; +using System.Collections.Generic; +using Microsoft.SolverFoundation.Common; +using Microsoft.SolverFoundation.Properties; +using Microsoft.SolverFoundation.Solvers; +using Microsoft.SolverFoundation.Services; +using Microsoft.Z3; +using System.Linq; +using System.Diagnostics; +using System.IO; + +namespace Microsoft.SolverFoundation.Plugin.Z3 +{ + /// + /// The class is implementation of the MSF constraint solver + /// using the Microsoft Z3 solver as the backend. + /// This solver supports Int, Real constraints and their arbitrary boolean combinations. + /// + public class Z3TermSolver : TermModel, ITermSolver, INonlinearSolution, IReportProvider + { + private NonlinearResult _result; + private Z3BaseSolver _solver; + + /// Constructor that initializes the base clases + public Z3TermSolver() : base(null) + { + _solver = new Z3BaseSolver(this); + } + + /// Constructor that initializes the base clases + public Z3TermSolver(ISolverEnvironment context) : this() { } + + /// + /// Shutdown can be called when when the solver is not active, i.e. + /// when it is done with Solve() or it has gracefully returns from Solve() + /// after an abort. + /// + public void Shutdown() { _solver.DestructSolver(true); } + + private BoolExpr MkBool(int rid) + { + var context = _solver.Context; + + if (IsConstant(rid)) + { + Rational lower, upper; + GetBounds(rid, out lower, out upper); + Debug.Assert(lower == upper); + if (lower.IsZero) return context.MkFalse(); + return context.MkTrue(); + } + if (IsOperation(rid)) + { + BoolExpr[] children; + ArithExpr[] operands; + TermModelOperation op = GetOperation(rid); + switch(op) { + case TermModelOperation.And: + Debug.Assert(GetOperandCount(rid) >= 2, "Conjunction requires at least two operands."); + children = (GetOperands(rid)).Select(x => MkBool(x)).ToArray(); + return context.MkAnd(children); + case TermModelOperation.Or: + Debug.Assert(GetOperandCount(rid) >= 2, "Disjunction requires at least two operands."); + children = (GetOperands(rid)).Select(x => MkBool(x)).ToArray(); + return context.MkOr(children); + case TermModelOperation.Not: + Debug.Assert(GetOperandCount(rid) == 1, "Negation is unary."); + return context.MkNot(MkBool(GetOperand(rid, 0))); + case TermModelOperation.If: + Debug.Assert(GetOperandCount(rid) == 3, "If is ternary."); + BoolExpr b = MkBool(GetOperand(rid, 0)); + Expr x1 = MkBool(GetOperand(rid, 1)); + Expr x2 = MkBool(GetOperand(rid, 2)); + return (BoolExpr)context.MkITE(b, x1, x2); + case TermModelOperation.Unequal: + Debug.Assert(GetOperandCount(rid) >= 2, "Distinct should have at least two operands."); + return context.MkDistinct((GetOperands(rid)).Select(x => MkTerm(x)).ToArray()); + case TermModelOperation.Greater: + case TermModelOperation.Less: + case TermModelOperation.GreaterEqual: + case TermModelOperation.LessEqual: + case TermModelOperation.Equal: + Debug.Assert(GetOperandCount(rid) >= 2, "Comparison should have at least two operands."); + operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); + return ReduceComparison(GetOperation(rid), operands); + case TermModelOperation.Identity: + Debug.Assert(GetOperandCount(rid) == 1, "Identity takes exactly one operand."); + return MkBool(GetOperand(rid, 0)); + default: + return context.MkEq(MkTerm(rid), _solver.GetNumeral(Rational.One)); + } + } + return context.MkEq(MkTerm(rid), _solver.GetNumeral(Rational.One)); + } + + private ArithExpr MkBoolToArith(BoolExpr e) + { + var context = _solver.Context; + return (ArithExpr)context.MkITE(e, _solver.GetNumeral(Rational.One), _solver.GetNumeral(Rational.Zero)); + } + + private ArithExpr MkTerm(int rid) + { + var context = _solver.Context; + + if (IsConstant(rid)) + { + Rational lower, upper; + GetBounds(rid, out lower, out upper); + Debug.Assert(lower == upper); + return _solver.GetNumeral(lower); + } + else if (IsOperation(rid)) + { + ArithExpr[] operands; + TermModelOperation op = GetOperation(rid); + switch(op) + { + case TermModelOperation.And: + case TermModelOperation.Or: + case TermModelOperation.Not: + case TermModelOperation.Unequal: + case TermModelOperation.Greater: + case TermModelOperation.Less: + case TermModelOperation.GreaterEqual: + case TermModelOperation.LessEqual: + case TermModelOperation.Equal: + return MkBoolToArith(MkBool(rid)); + case TermModelOperation.If: + Debug.Assert(GetOperandCount(rid) == 3, "If is ternary."); + BoolExpr b = MkBool(GetOperand(rid, 0)); + Expr x1 = MkTerm(GetOperand(rid, 1)); + Expr x2 = MkTerm(GetOperand(rid, 2)); + return (ArithExpr)context.MkITE(b, x1, x2); + case TermModelOperation.Plus: + Debug.Assert(GetOperandCount(rid) >= 2, "Plus takes at least two operands."); + operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); + return context.MkAdd(operands); + case TermModelOperation.Minus: + Debug.Assert(GetOperandCount(rid) == 1, "Minus takes exactly one operand."); + return context.MkUnaryMinus(MkTerm(GetOperand(rid, 0))); + case TermModelOperation.Times: + Debug.Assert(GetOperandCount(rid) >= 2, "Times requires at least two operands."); + operands = (GetOperands(rid)).Select(x => MkTerm(x)).ToArray(); + return context.MkMul(operands); + case TermModelOperation.Identity: + Debug.Assert(GetOperandCount(rid) == 1, "Identity takes exactly one operand."); + return MkTerm(GetOperand(rid, 0)); + case TermModelOperation.Abs: + Debug.Assert(GetOperandCount(rid) == 1, "Abs takes exactly one operand."); + ArithExpr e = MkTerm(GetOperand(rid, 0)); + ArithExpr minusE = context.MkUnaryMinus(e); + ArithExpr zero = _solver.GetNumeral(Rational.Zero); + return (ArithExpr)context.MkITE(context.MkGe(e, zero), e, minusE); + default: + Console.Error.WriteLine("{0} operation isn't supported.", op); + throw new NotSupportedException(); + } + } + else + { + return _solver.GetVariable(rid); + } + } + + private BoolExpr ReduceComparison(TermModelOperation type, ArithExpr[] operands) + { + var context = _solver.Context; + Debug.Assert(operands.Length >= 2); + Func mkComparison; + switch (type) + { + case TermModelOperation.Greater: + mkComparison = (x, y) => context.MkGt(x, y); + break; + case TermModelOperation.Less: + mkComparison = (x, y) => context.MkLt(x, y); + break; + case TermModelOperation.GreaterEqual: + mkComparison = (x, y) => context.MkGe(x, y); + break; + case TermModelOperation.LessEqual: + mkComparison = (x, y) => context.MkLe(x, y); + break; + case TermModelOperation.Equal: + mkComparison = (x, y) => context.MkEq(x, y); + break; + default: + throw new NotSupportedException(); + } + + BoolExpr current = mkComparison(operands[0], operands[1]); + for (int i = 1; i < operands.Length - 1; ++i) + current = context.MkAnd(current, mkComparison(operands[i], operands[i + 1])); + return current; + } + + private bool IsBoolRow(int rid) + { + Rational lower, upper; + GetBounds(rid, out lower, out upper); + + return lower == upper && lower.IsOne && IsBoolTerm(rid); + } + + private bool IsBoolTerm(int rid) + { + if (IsConstant(rid)) + { + Rational lower, upper; + GetBounds(rid, out lower, out upper); + Debug.Assert(lower == upper); + return lower.IsOne || lower.IsZero; + } + if (IsOperation(rid)) + { + TermModelOperation op = GetOperation(rid); + switch (op) + { + case TermModelOperation.And: + case TermModelOperation.Or: + case TermModelOperation.Not: + case TermModelOperation.LessEqual: + case TermModelOperation.Less: + case TermModelOperation.Greater: + case TermModelOperation.GreaterEqual: + case TermModelOperation.Unequal: + case TermModelOperation.Equal: + return true; + case TermModelOperation.If: + return IsBoolTerm(GetOperand(rid, 1)) && + IsBoolTerm(GetOperand(rid, 2)); + case TermModelOperation.Identity: + return IsBoolTerm(GetOperand(rid, 0)); + default: + return false; + } + } + return false; + } + + /// + /// Adds a MSF row to the Z3 assertions. + /// + /// The MSF row id + private void AddRow(int rid) + { + if (IsConstant(rid)) + return; + + if (IsBoolRow(rid)) + { + _solver.AssertBool(MkBool(rid)); + return; + } + // Start with the 0 term + ArithExpr row = MkTerm(rid); + _solver.AssertArith(rid, row); + } + + private TermModelOperation[] _supportedOperations = + { TermModelOperation.And, + TermModelOperation.Or, + TermModelOperation.Not, + TermModelOperation.Unequal, + TermModelOperation.Greater, + TermModelOperation.Less, + TermModelOperation.GreaterEqual, + TermModelOperation.LessEqual, + TermModelOperation.Equal, + TermModelOperation.If, + TermModelOperation.Plus, + TermModelOperation.Minus, + TermModelOperation.Times, + TermModelOperation.Identity, + TermModelOperation.Abs }; + + /// + /// Gets the operations supported by the solver. + /// + /// All the TermModelOperations supported by the solver. + public IEnumerable SupportedOperations + { + get { return _supportedOperations; } + } + + /// + /// Set results based on internal solver status + /// + private void SetResult(Z3Result status) + { + switch (status) + { + case Z3Result.Optimal: + _result = NonlinearResult.Optimal; + break; + case Z3Result.LocalOptimal: + _result = NonlinearResult.LocalOptimal; + break; + case Z3Result.Feasible: + _result = NonlinearResult.Feasible; + break; + case Z3Result.Infeasible: + _result = NonlinearResult.Infeasible; + break; + case Z3Result.Interrupted: + _result = NonlinearResult.Interrupted; + break; + default: + Debug.Assert(false, "Unrecognized Z3 Result"); + break; + } + } + + /// + /// Starts solving the problem using the Z3 solver. + /// + /// Parameters to the solver + /// The solution to the problem + public INonlinearSolution Solve(ISolverParameters parameters) + { + // Get the Z3 parameters + var z3Params = parameters as Z3BaseParams; + Debug.Assert(z3Params != null, "Parameters should be an instance of Z3BaseParams."); + + _solver.Solve(z3Params, Goals, AddRow, MkTerm, SetResult); + + return this; + } + + double INonlinearSolution.GetValue(int vid) + { + Debug.Assert(_solver.Variables.ContainsKey(vid), "This index should correspond to a variable."); + return GetValue(vid).ToDouble(); + } + + public int SolvedGoalCount + { + get { return GoalCount; } + } + + public double GetSolutionValue(int goalIndex) + { + var goal = Goals.ElementAt(goalIndex); + Debug.Assert(goal != null, "Goal should be an element of the goal list."); + return GetValue(goal.Index).ToDouble(); + } + + public void GetSolvedGoal(int goalIndex, out object key, out int vid, out bool minimize, out bool optimal) + { + var goal = Goals.ElementAt(goalIndex); + Debug.Assert(goal != null, "Goal should be an element of the goal list."); + key = goal.Key; + vid = goal.Index; + minimize = goal.Minimize; + optimal = _result == NonlinearResult.Optimal; + } + + public NonlinearResult Result + { + get { return _result; } + } + + public Report GetReport(SolverContext context, Solution solution, SolutionMapping solutionMapping) + { + PluginSolutionMapping pluginSolutionMapping = solutionMapping as PluginSolutionMapping; + if (pluginSolutionMapping == null && solutionMapping != null) + throw new ArgumentException("solutionMapping is not a LinearSolutionMapping", "solutionMapping"); + return new Z3TermSolverReport(context, this, solution, pluginSolutionMapping); + } + } + + public class Z3TermSolverReport : Report + { + public Z3TermSolverReport(SolverContext context, ISolver solver, Solution solution, PluginSolutionMapping pluginSolutionMapping) + : base(context, solver, solution, pluginSolutionMapping) + { + } + } +} diff --git a/examples/msf/Validator/App.config b/examples/msf/Validator/App.config new file mode 100644 index 000000000..75e2872f1 --- /dev/null +++ b/examples/msf/Validator/App.config @@ -0,0 +1,60 @@ + + + +
+ + + + + + + + + + + + + + + + diff --git a/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config b/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config new file mode 100644 index 000000000..cd9dcad25 --- /dev/null +++ b/examples/msf/Validator/MicrosoftSolverFoundationForExcel.dll.config @@ -0,0 +1,58 @@ + + + +
+ + + + + + + + + + + + + diff --git a/examples/msf/Validator/Program.cs b/examples/msf/Validator/Program.cs new file mode 100644 index 000000000..8afb28af5 --- /dev/null +++ b/examples/msf/Validator/Program.cs @@ -0,0 +1,200 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +using System; +using System.IO; +using System.Linq; +using System.Collections.Generic; +using Microsoft.SolverFoundation.Common; +using Microsoft.SolverFoundation.Solvers; +using Microsoft.SolverFoundation.Plugin.Z3; +using Microsoft.SolverFoundation.Services; +using System.Text; + +namespace Validator +{ + class Program + { + static void LoadModel(SolverContext context, string fileName) + { + string ext = Path.GetExtension(fileName).ToLower(); + + if (ext == ".mps") + { + context.LoadModel(FileFormat.MPS, Path.GetFullPath(fileName)); + } + else if (ext == ".smps") + { + context.LoadModel(FileFormat.SMPS, Path.GetFullPath(fileName)); + } + else if (ext == ".oml") + { + context.LoadModel(FileFormat.OML, Path.GetFullPath(fileName)); + } + else + { + throw new NotSupportedException("This file format hasn't been supported."); + } + } + + static void ExecuteZ3(string fileName, Z3BaseDirective directive) + { + SolverContext context = SolverContext.GetContext(); + try + { + LoadModel(context, fileName); + + Solution solution = context.Solve(directive); + Report report = solution.GetReport(); + Console.Write("{0}", report); + } + catch (Exception e) + { + Console.WriteLine("Skipping unsolvable instance in {0} with error message '{1}'.", fileName, e.Message); + } + finally + { + context.ClearModel(); + } + } + + static void ConvertToSMT2(string fileName, Z3BaseDirective directive) + { + SolverContext context = SolverContext.GetContext(); + try + { + LoadModel(context, fileName); + + if (context.CurrentModel.Goals.Any()) + { + directive.SMT2LogFile = Path.ChangeExtension(fileName, ".smt2"); + context.Solve(() => true, directive); + } + } + catch (Exception e) + { + Console.WriteLine("Skipping unconvertable instance in {0} with error message '{1}'.", fileName, e.Message); + } + finally + { + context.ClearModel(); + } + } + + static void ValidateZ3(string fileName, Z3BaseDirective directive) + { + SolverContext context = SolverContext.GetContext(); + try + { + LoadModel(context, fileName); + + if (context.CurrentModel.Goals.Any()) + { + var msfDirective = (directive is Z3MILPDirective) ? (Directive)new MixedIntegerProgrammingDirective() { TimeLimit = 10000 } + : (Directive)new Directive() { TimeLimit = 10000 }; + var sol1 = context.Solve(msfDirective); + + Console.WriteLine("Solved the model using MSF."); + Console.Write("{0}", sol1.GetReport()); + var expectedGoals = sol1.Goals.Select(x => x.ToDouble()); + context.ClearModel(); + + context.LoadModel(FileFormat.OML, Path.GetFullPath(fileName)); + directive.SMT2LogFile = Path.ChangeExtension(fileName, ".smt2"); + var sol2 = context.Solve(directive); + //Console.Write("{0}", sol2.GetReport()); + var actualGoals = sol2.Goals.Select(x => x.ToDouble()); + + Console.WriteLine("Solved the model using Z3."); + var goalPairs = expectedGoals.Zip(actualGoals, (expected, actual) => new { expected, actual }).ToArray(); + bool validated = goalPairs.All(p => Math.Abs(p.expected - p.actual) <= 0.0001); + if (validated) + { + Console.WriteLine("INFO: Two solvers give approximately the same results."); + } + else + { + Console.Error.WriteLine("ERROR: Discrepancy found between results."); + if (!validated && File.Exists(directive.SMT2LogFile)) + { + var sb = new StringBuilder(); + for(int i = 0; i < goalPairs.Length; i++) + { + sb.AppendFormat("\n(echo \"Goal {0}: actual |-> {1:0.0000}, expected |-> {2:0.0000}\")", + i + 1, goalPairs[i].actual, goalPairs[i].expected); + } + Console.Error.WriteLine(sb.ToString()); + File.AppendAllText(directive.SMT2LogFile, sb.ToString()); + } + } + } + else + { + Console.WriteLine("Ignoring this instance without having any goal."); + } + } + catch (Exception e) + { + Console.WriteLine("Skipping unsolvable instance in {0} with error message '{1}'.", + fileName, e.Message); + } + finally + { + context.ClearModel(); + } + } + + static void Main(string[] args) + { + Z3BaseDirective directive = new Z3MILPDirective(); + + for (int i = 0; i < args.Length; ++i) { + if (args[i] == "-s" || args[i] == "-solve") + { + ExecuteZ3(args[i + 1], directive); + return; + } + if (args[i] == "-c" || args[i] == "-convert") + { + ConvertToSMT2(args[i + 1], directive); + return; + } + if (args[i] == "-v" || args[i] == "-validate") + { + ValidateZ3(args[i + 1], directive); + return; + } + if (args[i] == "-t" || args[i] == "-term") + { + directive = new Z3TermDirective(); + } + } + + if (args.Length > 0) + { + ExecuteZ3(args[0], directive); + return; + } + + Console.WriteLine(@" +Validator is a simple command line to migrate benchmarks from OML, MPS and SMPS to SMT2 formats. + +Commands: + -solve : solving the model using Z3 + -convert : converting the model into SMT2 format + -validate : validating by comparing results between Z3 and MSF solvers + -term : change the default Z3 MILP solver to Z3 Term solver + + where is any file with OML, MPS or SMPS extension. + +Examples: + Validator.exe -convert model.mps + Validator.exe -term -solve model.oml + +"); + } + } +} diff --git a/examples/msf/Validator/Properties/AssemblyInfo.cs b/examples/msf/Validator/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..eb2f8ed71 --- /dev/null +++ b/examples/msf/Validator/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("testSolver")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("testSolver")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c03c1084-d119-483f-80fe-c639eae75959")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/examples/msf/Validator/Validator.csproj b/examples/msf/Validator/Validator.csproj new file mode 100644 index 000000000..cfea3c80b --- /dev/null +++ b/examples/msf/Validator/Validator.csproj @@ -0,0 +1,123 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {54835857-129F-44C9-B529-A42158647B36} + Exe + Properties + Validator + Validator + v4.0 + 512 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x86 + true + GlobalSuppressions.cs + prompt + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + true + GlobalSuppressions.cs + prompt + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + ..\Microsoft.Solver.Foundation.dll + + + + + + + + + + + + + + + + + + + + + + {7340e664-f648-4ff7-89b2-f4da424996d3} + SolverFoundation.Plugin.Z3 + + + + + \ No newline at end of file diff --git a/examples/msf/Z3MSFPlugin.sln b/examples/msf/Z3MSFPlugin.sln new file mode 100644 index 000000000..c3af1dc22 --- /dev/null +++ b/examples/msf/Z3MSFPlugin.sln @@ -0,0 +1,125 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverFoundation.Plugin.Z3", "SolverFoundation.Plugin.Z3\SolverFoundation.Plugin.Z3.csproj", "{7340E664-F648-4FF7-89B2-F4DA424996D3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverFoundation.Plugin.Z3.Tests", "SolverFoundation.Plugin.Z3.Tests\SolverFoundation.Plugin.Z3.Tests.csproj", "{280AEE2F-1FDB-4A27-BE37-14DC154C873B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Validator", "Validator\Validator.csproj", "{54835857-129F-44C9-B529-A42158647B36}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F1E99540-BA5E-46DF-9E29-6146A309CD18}" + ProjectSection(SolutionItems) = preProject + README = README + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + commercial_64|Any CPU = commercial_64|Any CPU + commercial_64|Mixed Platforms = commercial_64|Mixed Platforms + commercial_64|x64 = commercial_64|x64 + commercial_64|x86 = commercial_64|x86 + commercial|Any CPU = commercial|Any CPU + commercial|Mixed Platforms = commercial|Mixed Platforms + commercial|x64 = commercial|x64 + commercial|x86 = commercial|x86 + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Any CPU.ActiveCfg = commercial_64|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Any CPU.Build.0 = commercial_64|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Mixed Platforms.ActiveCfg = commercial_64|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|Mixed Platforms.Build.0 = commercial_64|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x64.ActiveCfg = commercial_64|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x86.ActiveCfg = commercial_64|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial_64|x86.Build.0 = commercial_64|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Any CPU.ActiveCfg = commercial|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Any CPU.Build.0 = commercial|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Mixed Platforms.ActiveCfg = commercial|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|Mixed Platforms.Build.0 = commercial|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x64.ActiveCfg = commercial|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x86.ActiveCfg = commercial|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.commercial|x86.Build.0 = commercial|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x64.ActiveCfg = Debug|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x86.ActiveCfg = Debug|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Debug|x86.Build.0 = Debug|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Any CPU.Build.0 = Release|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|Mixed Platforms.Build.0 = Release|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x64.ActiveCfg = Release|Any CPU + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x86.ActiveCfg = Release|x86 + {7340E664-F648-4FF7-89B2-F4DA424996D3}.Release|x86.Build.0 = Release|x86 + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Any CPU.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Any CPU.Build.0 = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Mixed Platforms.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|Mixed Platforms.Build.0 = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|x64.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial_64|x86.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Any CPU.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Any CPU.Build.0 = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Mixed Platforms.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|Mixed Platforms.Build.0 = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|x64.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.commercial|x86.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x64.ActiveCfg = Debug|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x86.ActiveCfg = Debug|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Debug|x86.Build.0 = Debug|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Any CPU.Build.0 = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x64.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x86.ActiveCfg = Release|Any CPU + {280AEE2F-1FDB-4A27-BE37-14DC154C873B}.Release|x86.Build.0 = Release|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Any CPU.ActiveCfg = Release|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Any CPU.Build.0 = Release|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Mixed Platforms.ActiveCfg = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.commercial_64|Mixed Platforms.Build.0 = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x64.ActiveCfg = Release|x64 + {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x64.Build.0 = Release|x64 + {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x86.ActiveCfg = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.commercial_64|x86.Build.0 = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.commercial|Any CPU.ActiveCfg = Release|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.commercial|Any CPU.Build.0 = Release|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.commercial|Mixed Platforms.ActiveCfg = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.commercial|Mixed Platforms.Build.0 = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.commercial|x64.ActiveCfg = Release|x64 + {54835857-129F-44C9-B529-A42158647B36}.commercial|x64.Build.0 = Release|x64 + {54835857-129F-44C9-B529-A42158647B36}.commercial|x86.ActiveCfg = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.commercial|x86.Build.0 = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {54835857-129F-44C9-B529-A42158647B36}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {54835857-129F-44C9-B529-A42158647B36}.Debug|x64.ActiveCfg = Debug|x64 + {54835857-129F-44C9-B529-A42158647B36}.Debug|x64.Build.0 = Debug|x64 + {54835857-129F-44C9-B529-A42158647B36}.Debug|x86.ActiveCfg = Debug|x86 + {54835857-129F-44C9-B529-A42158647B36}.Debug|x86.Build.0 = Debug|x86 + {54835857-129F-44C9-B529-A42158647B36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.Release|Any CPU.Build.0 = Release|Any CPU + {54835857-129F-44C9-B529-A42158647B36}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.Release|Mixed Platforms.Build.0 = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.Release|x64.ActiveCfg = Release|x64 + {54835857-129F-44C9-B529-A42158647B36}.Release|x64.Build.0 = Release|x64 + {54835857-129F-44C9-B529-A42158647B36}.Release|x86.ActiveCfg = Release|x86 + {54835857-129F-44C9-B529-A42158647B36}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/python/complex/complex.py b/examples/python/complex/complex.py index 5d6283745..467d76c55 100644 --- a/examples/python/complex/complex.py +++ b/examples/python/complex/complex.py @@ -47,13 +47,13 @@ class ComplexExpr: return ComplexExpr(other.r*self.r - other.i*self.i, other.i*self.r + other.r*self.i) def __pow__(self, k): - if k == 0: - return ComplexExpr(1, 0) - if k == 1: - return self - if k < 0: - return (self ** (-k)).inv() - return reduce(lambda x, y: x * y, [self for _ in xrange(k)], ComplexExpr(1, 0)) + if k == 0: + return ComplexExpr(1, 0) + if k == 1: + return self + if k < 0: + return (self ** (-k)).inv() + return reduce(lambda x, y: x * y, [self for _ in xrange(k)], ComplexExpr(1, 0)) def inv(self): den = self.r*self.r + self.i*self.i diff --git a/examples/python/example.py b/examples/python/example.py index 879c17c43..e0c9374e7 100644 --- a/examples/python/example.py +++ b/examples/python/example.py @@ -1,3 +1,5 @@ +# Copyright (c) Microsoft Corporation 2015 + from z3 import * x = Real('x') diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index 00d03a8b0..0ed92801d 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include #include #include @@ -809,8 +815,12 @@ class env { r = terms[0] / terms[1]; } else if (!strcmp(ch,"$distinct")) { - check_arity(terms.size(), 2); - r = terms[0] != terms[1]; + if (terms.size() == 2) { + r = terms[0] != terms[1]; + } + else { + r = distinct(terms); + } } else if (!strcmp(ch,"$floor") || !strcmp(ch,"$to_int")) { check_arity(terms.size(), 1); @@ -1089,12 +1099,11 @@ class env { } z3::sort mk_sort(char const* s) { - z3::symbol sym = symbol(s); - return mk_sort(sym); + return m_context.uninterpreted_sort(s); } z3::sort mk_sort(z3::symbol& s) { - return z3::sort(m_context, Z3_mk_uninterpreted_sort(m_context, s)); + return m_context.uninterpreted_sort(s); } public: @@ -2083,7 +2092,7 @@ bool parse_is_sat_line(char const* line, bool& is_sat) { return true; } return false; -} +} bool parse_is_sat(char const* filename, bool& is_sat) { std::ifstream is(filename); diff --git a/examples/tptp/tptp5.h b/examples/tptp/tptp5.h index 69fe79b28..948664243 100644 --- a/examples/tptp/tptp5.h +++ b/examples/tptp/tptp5.h @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifndef TPTP5_H_ #define TPTP5_H_ diff --git a/examples/tptp/tptp5.lex.cpp b/examples/tptp/tptp5.lex.cpp index 639d92750..afeebb30a 100644 --- a/examples/tptp/tptp5.lex.cpp +++ b/examples/tptp/tptp5.lex.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #line 2 "tptp5.lex.cpp" #line 4 "tptp5.lex.cpp" diff --git a/scripts/mk_copyright.py b/scripts/mk_copyright.py new file mode 100644 index 000000000..4d774cb96 --- /dev/null +++ b/scripts/mk_copyright.py @@ -0,0 +1,57 @@ +# Copyright (c) 2015 Microsoft Corporation + +import os +import re + +cr = re.compile("Copyright") +aut = re.compile("Automatically generated") + +cr_notice = """ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +""" + +def has_cr(file): + ins = open(file) + lines = 0 + line = ins.readline() + while line and lines < 20: + m = cr.search(line) + if m: + ins.close() + return True + m = aut.search(line) + if m: + ins.close() + return True + line = ins.readline() + ins.close() + return False + +def add_cr(file): + tmp = "%s.tmp" % file + ins = open(file) + ous = open(tmp,'w') + ous.write(cr_notice) + line = ins.readline() + while line: + ous.write(line) + line = ins.readline() + ins.close() + ous.close() + os.system("move %s %s" % (tmp, file)) + +def add_missing_cr(dir): + for root, dirs, files in os.walk(dir): + for f in files: + if f.endswith('.cpp') or f.endswith('.h') or f.endswith('.c') or f.endswith('.cs'): + path = "%s\\%s" % (root, f) + if not has_cr(path): + print "Missing CR for %s" % path + add_cr(path) + +add_missing_cr('src') +add_missing_cr('examples') diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 1e21ff810..21a00e697 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -9,12 +9,13 @@ from mk_util import * # Z3 Project definition def init_project_def(): - set_version(4, 4, 0, 0) + set_version(4, 4, 1, 0) add_lib('util', []) add_lib('polynomial', ['util'], 'math/polynomial') add_lib('sat', ['util']) add_lib('nlsat', ['polynomial', 'sat']) add_lib('hilbert', ['util'], 'math/hilbert') + add_lib('simplex', ['util'], 'math/simplex') add_lib('interval', ['util'], 'math/interval') add_lib('realclosure', ['interval'], 'math/realclosure') add_lib('subpaving', ['interval'], 'math/subpaving') @@ -49,37 +50,38 @@ def init_project_def(): add_lib('smt_params', ['ast', 'simplifier', 'pattern', 'bit_blaster'], 'smt/params') add_lib('proto_model', ['model', 'simplifier', 'smt_params'], 'smt/proto_model') add_lib('smt', ['bit_blaster', 'macros', 'normal_forms', 'cmd_context', 'proto_model', - 'substitution', 'grobner', 'euclid', 'proof_checker', 'pattern', 'parser_util', 'fpa']) + 'substitution', 'grobner', 'euclid', 'simplex', 'proof_checker', 'pattern', 'parser_util', 'fpa']) add_lib('user_plugin', ['smt'], 'smt/user_plugin') add_lib('bv_tactics', ['tactic', 'bit_blaster'], 'tactic/bv') add_lib('fuzzing', ['ast'], 'test/fuzzing') add_lib('smt_tactic', ['smt'], 'smt/tactic') - add_lib('fpa_tactics', ['fpa', 'core_tactics', 'bv_tactics', 'sat_tactic', 'smt_tactic'], 'tactic/fpa') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') add_lib('qe', ['smt','sat'], 'qe') add_lib('duality', ['smt', 'interp', 'qe']) add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') - add_lib('transforms', ['muz', 'hilbert'], 'muz/transforms') + add_lib('dataflow', ['muz'], 'muz/dataflow') + add_lib('transforms', ['muz', 'hilbert', 'dataflow'], 'muz/transforms') add_lib('rel', ['muz', 'transforms'], 'muz/rel') - add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'smt_tactic'], 'muz/pdr') + add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'core_tactics', 'smt_tactic'], 'muz/pdr') add_lib('clp', ['muz', 'transforms'], 'muz/clp') add_lib('tab', ['muz', 'transforms'], 'muz/tab') add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') + add_lib('ddnf', ['muz', 'transforms', 'rel'], 'muz/ddnf') add_lib('duality_intf', ['muz', 'transforms', 'duality'], 'muz/duality') - add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf'], 'muz/fp') - add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') + add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf', 'ddnf'], 'muz/fp') + add_lib('nlsat_smt_tactic', ['nlsat_tactic', 'smt_tactic'], 'tactic/nlsat_smt') + add_lib('smtlogic_tactics', ['arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe','nlsat_smt_tactic'], 'tactic/smtlogics') + add_lib('fpa_tactics', ['fpa', 'core_tactics', 'bv_tactics', 'sat_tactic', 'smt_tactic', 'arith_tactics', 'smtlogic_tactics'], 'tactic/fpa') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') - add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') + add_lib('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') + add_lib('portfolio', ['smtlogic_tactics', 'sat_solver', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') -# add_dll('foci2', ['util'], 'interp/foci2stub', -# dll_name='foci2', -# export_files=['foci2stub.cpp']) -# add_lib('interp', ['solver','foci2']) + add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic', 'sat_solver'], 'opt') API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_interp.h', 'z3_fpa.h'] - add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure', 'interp'], + add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure', 'interp', 'opt'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) - add_exe('shell', ['api', 'sat', 'extra_cmds'], exe_name='z3') - add_exe('test', ['api', 'fuzzing'], exe_name='test-z3', install=False) + add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3') + add_exe('test', ['api', 'fuzzing', 'simplex'], exe_name='test-z3', install=False) add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll', reexports=['api'], dll_name='libz3', diff --git a/scripts/mk_util.py b/scripts/mk_util.py index e40a549a1..87fe7f16d 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -85,6 +85,11 @@ VS_PAR=False VS_PAR_NUM=8 GPROF=False GIT_HASH=False +SLOW_OPTIMIZE=False +USE_OMP=True + +FPMATH="Default" +FPMATH_FLAGS="-mfpmath=sse -msse -msse2" def check_output(cmd): return str(subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]).rstrip('\r\n') @@ -221,6 +226,8 @@ def test_foci2(cc,foci2lib): return exec_compiler_cmd([cc, CPPFLAGS, '-Isrc/interp', 'tstfoci2.cpp', LDFLAGS, foci2lib]) == 0 def test_openmp(cc): + if not USE_OMP: + return False if is_verbose(): print("Testing OpenMP...") t = TempFile('tstomp.cpp') @@ -237,6 +244,27 @@ def test_openmp(cc): else: return exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '-fopenmp']) == 0 +def test_fpmath(cc): + global FPMATH_FLAGS + if is_verbose(): + print("Testing floating point support...") + t = TempFile('tstsse.cpp') + t.add('int main() { return 42; }\n') + t.commit() + if exec_compiler_cmd([cc, CPPFLAGS, 'tstsse.cpp', LDFLAGS, '-mfpmath=sse -msse -msse2']) == 0: + FPMATH_FLAGS="-mfpmath=sse -msse -msse2" + return "SSE2-GCC" + elif exec_compiler_cmd([cc, CPPFLAGS, 'tstsse.cpp', LDFLAGS, '-msse -msse2']) == 0: + FPMATH_FLAGS="-msse -msse2" + return "SSE2-CLANG" + elif exec_compiler_cmd([cc, CPPFLAGS, 'tstsse.cpp', LDFLAGS, '-mfpu=vfp -mfloat-abi=hard']) == 0: + FPMATH_FLAGS="-mfpu=vfp -mfloat-abi=hard" + return "ARM-VFP" + else: + FPMATH_FLAGS="" + return "UNKNOWN" + + def find_jni_h(path): for root, dirs, files in os.walk(path): for f in files: @@ -527,6 +555,8 @@ def display_help(exit_code): print(" -v, --vsproj generate Visual Studio Project Files.") if IS_WINDOWS: print(" -n, --nodotnet do not generate Microsoft.Z3.dll make rules.") + if IS_WINDOWS: + print(" --optimize generate optimized code during linking.") print(" -j, --java generate Java bindings.") print(" --ml generate OCaml bindings.") print(" --staticlib build Z3 static library.") @@ -534,6 +564,7 @@ def display_help(exit_code): print(" -g, --gmp use GMP.") print(" --gprof enable gprof") print(" -f --foci2= use foci2 library at path") + print(" --noomp disable OpenMP and all features that require it.") print("") print("Some influential environment variables:") if not IS_WINDOWS: @@ -553,13 +584,13 @@ def display_help(exit_code): def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM global DOTNET_ENABLED, JAVA_ENABLED, ML_ENABLED, STATIC_LIB, PREFIX, GMP, FOCI2, FOCI2LIB, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH - global LINUX_X64 + global LINUX_X64, SLOW_OPTIMIZE, USE_OMP try: options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:df:sxhmcvtnp:gj', ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', 'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'foci2=', 'java', 'parallel=', 'gprof', - 'githash=', 'x86', 'ml']) + 'githash=', 'x86', 'ml', 'optimize', 'noomp']) except: print("ERROR: Invalid command line option") display_help(1) @@ -594,6 +625,8 @@ def parse_options(): DOTNET_ENABLED = False elif opt in ('--staticlib'): STATIC_LIB = True + elif opt in ('--optimize'): + SLOW_OPTIMIZE = True elif not IS_WINDOWS and opt in ('-p', '--prefix'): PREFIX = arg PYTHON_PACKAGE_DIR = os.path.join(PREFIX, 'lib', 'python%s' % distutils.sysconfig.get_python_version(), 'dist-packages') @@ -616,6 +649,8 @@ def parse_options(): GIT_HASH=arg elif opt in ('', '--ml'): ML_ENABLED = True + elif opt in ('', '--noomp'): + USE_OMP = False else: print("ERROR: Invalid command line option '%s'" % opt) display_help(1) @@ -1722,7 +1757,6 @@ def mk_config(): 'OBJ_EXT=.obj\n' 'LIB_EXT=.lib\n' 'AR=lib\n' - 'AR_FLAGS=/nologo /LTCG\n' 'AR_OUTFLAG=/OUT:\n' 'EXE_EXT=.exe\n' 'LINK=cl\n' @@ -1741,39 +1775,44 @@ def mk_config(): extra_opt = ' %s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH) if DEBUG_MODE: config.write( + 'AR_FLAGS=/nologo\n' 'LINK_FLAGS=/nologo /MDd\n' 'SLINK_FLAGS=/nologo /LDd\n') if not VS_X64: config.write( - 'CXXFLAGS=/c /GL /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % extra_opt) + 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % extra_opt) config.write( - 'LINK_EXTRA_FLAGS=/link /LTCG /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' - 'SLINK_EXTRA_FLAGS=/link /LTCG /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') + 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' + 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') else: config.write( - 'CXXFLAGS=/c /GL /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _AMD64_ /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze-\n' % extra_opt) + 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _AMD64_ /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze-\n' % extra_opt) config.write( - 'LINK_EXTRA_FLAGS=/link /LTCG /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' - 'SLINK_EXTRA_FLAGS=/link /LTCG /DEBUG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') + 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' + 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') else: # Windows Release mode + LTCG=' /LTCG' if SLOW_OPTIMIZE else '' + GL = ' /GL' if SLOW_OPTIMIZE else '' config.write( + 'AR_FLAGS=/nologo%s\n' 'LINK_FLAGS=/nologo /MD\n' - 'SLINK_FLAGS=/nologo /LD\n') + 'SLINK_FLAGS=/nologo /LD\n' + % LTCG) if TRACE: extra_opt = '%s /D _TRACE ' % extra_opt if not VS_X64: config.write( - 'CXXFLAGS=/nologo /c /GL /Zi /W3 /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _CONSOLE /D _WINDOWS /D ASYNC_COMMANDS /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % extra_opt) + 'CXXFLAGS=/nologo /c%s /Zi /W3 /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _CONSOLE /D _WINDOWS /D ASYNC_COMMANDS /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % (GL, extra_opt)) config.write( - 'LINK_EXTRA_FLAGS=/link /LTCG /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' - 'SLINK_EXTRA_FLAGS=/link /LTCG /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') + 'LINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' + 'SLINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n' % (LTCG, LTCG)) else: config.write( - 'CXXFLAGS=/c /GL /Zi /nologo /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _LIB /D _WINDOWS /D _AMD64_ /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TP\n' % extra_opt) + 'CXXFLAGS=/c%s /Zi /nologo /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _LIB /D _WINDOWS /D _AMD64_ /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TP\n' % (GL, extra_opt)) config.write( - 'LINK_EXTRA_FLAGS=/link /LTCG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608\n' - 'SLINK_EXTRA_FLAGS=/link /LTCG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608\n') + 'LINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608\n' + 'SLINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608\n' % (LTCG, LTCG)) # End of Windows VS config.mk if is_verbose(): @@ -1787,7 +1826,7 @@ def mk_config(): print('OCaml Native: %s' % OCAMLOPT) print('OCaml Library: %s' % OCAML_LIB) else: - global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG + global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS OS_DEFINES = "" ARITH = "internal" check_ar() @@ -1816,9 +1855,11 @@ def mk_config(): if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) CXXFLAGS = '%s -fvisibility=hidden -c' % CXXFLAGS + FPMATH = test_fpmath(CXX) + CXXFLAGS = '%s %s' % (CXXFLAGS, FPMATH_FLAGS) HAS_OMP = test_openmp(CXX) if HAS_OMP: - CXXFLAGS = '%s -fopenmp -mfpmath=sse' % CXXFLAGS + CXXFLAGS = '%s -fopenmp' % CXXFLAGS LDFLAGS = '%s -fopenmp' % LDFLAGS SLIBEXTRAFLAGS = '%s -fopenmp' % SLIBEXTRAFLAGS else: @@ -1839,7 +1880,7 @@ def mk_config(): SLIBFLAGS = '-dynamiclib' elif sysname == 'Linux': CXXFLAGS = '%s -fno-strict-aliasing -D_LINUX_' % CXXFLAGS - OS_DEFINES = '-D_LINUX' + OS_DEFINES = '-D_LINUX_' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' @@ -1859,7 +1900,8 @@ def mk_config(): else: raise MKException('Unsupported platform: %s' % sysname) if is64(): - CXXFLAGS = '%s -fPIC' % CXXFLAGS + if sysname[:6] != 'CYGWIN': + CXXFLAGS = '%s -fPIC' % CXXFLAGS CPPFLAGS = '%s -D_AMD64_' % CPPFLAGS if sysname == 'Linux': CPPFLAGS = '%s -D_USE_THREAD_LOCAL' % CPPFLAGS @@ -1871,7 +1913,6 @@ def mk_config(): CPPFLAGS = '%s -DZ3DEBUG' % CPPFLAGS if TRACE or DEBUG_MODE: CPPFLAGS = '%s -D_TRACE' % CPPFLAGS - CXXFLAGS = '%s -msse -msse2' % CXXFLAGS config.write('PREFIX=%s\n' % PREFIX) config.write('CC=%s\n' % CC) config.write('CXX=%s\n' % CXX) @@ -1902,6 +1943,7 @@ def mk_config(): print('OpenMP: %s' % HAS_OMP) print('Prefix: %s' % PREFIX) print('64-bit: %s' % is64()) + print('FP math: %s' % FPMATH) if GPROF: print('gprof: enabled') print('Python version: %s' % distutils.sysconfig.get_python_version()) diff --git a/scripts/mk_win_dist.py b/scripts/mk_win_dist.py index db4cc48e3..fb42750e3 100644 --- a/scripts/mk_win_dist.py +++ b/scripts/mk_win_dist.py @@ -180,7 +180,7 @@ def mk_dist_dir_core(x64): mk_util.JAVA_ENABLED = JAVA_ENABLED mk_win_dist(build_path, dist_path) if is_verbose(): - print("Generated %s distribution folder at '%s'") % (platform, dist_path) + print("Generated %s distribution folder at '%s'" % (platform, dist_path)) def mk_dist_dir(): mk_dist_dir_core(False) @@ -208,7 +208,7 @@ def mk_zip_core(x64): ZIPOUT = zipfile.ZipFile(zfname, 'w', zipfile.ZIP_DEFLATED) os.path.walk(dist_path, mk_zip_visitor, '*') if is_verbose(): - print("Generated '%s'") % zfname + print("Generated '%s'" % zfname) except: pass ZIPOUT = None @@ -253,7 +253,7 @@ def cp_vs_runtime_core(x64): for f in VS_RUNTIME_FILES: shutil.copy(f, bin_dist_path) if is_verbose(): - print("Copied '%s' to '%s'") % (f, bin_dist_path) + print("Copied '%s' to '%s'" % (f, bin_dist_path)) def cp_vs_runtime(): cp_vs_runtime_core(True) diff --git a/scripts/trackall.sh b/scripts/trackall.sh index d8351af24..5e637100d 100755 --- a/scripts/trackall.sh +++ b/scripts/trackall.sh @@ -1,7 +1,9 @@ #!/bin/bash +# Copyright (c) 2015 Microsoft Corporation # Script for "cloning" (and tracking) all branches at codeplex. # On Windows, this script must be executed in the "git Bash" console. + for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master`; do git branch --track ${branch##*/} $branch done diff --git a/scripts/update_api.py b/scripts/update_api.py index e179043e2..0d57a29ac 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -568,10 +568,12 @@ def mk_java(): java_native.write(' public static class ObjArrayPtr { public long[] value; }\n') java_native.write(' public static class UIntArrayPtr { public int[] value; }\n') java_native.write(' public static native void setInternalErrorHandler(long ctx);\n\n') - if IS_WINDOWS or os.uname()[0]=="CYGWIN": - java_native.write(' static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name) - else: - java_native.write(' static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name[3:]) # We need 3: to extract the prexi 'lib' form the dll_name + + java_native.write(' static {\n') + java_native.write(' try { System.loadLibrary("z3java"); }\n') + java_native.write(' catch (UnsatisfiedLinkError ex) { System.loadLibrary("libz3java"); }\n') + java_native.write(' }\n') + java_native.write('\n') for name, result, params in _dotnet_decls: java_native.write(' protected static native %s INTERNAL%s(' % (type2java(result), java_method_name(name))) @@ -1010,6 +1012,11 @@ def def_API(name, result, params): log_c.write(" }\n") log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_uint_array(%s)" % i) + elif ty == INT: + log_c.write("U(a%s[i]);" % i) + log_c.write(" }\n") + log_c.write(" Au(a%s);\n" % sz) + exe_c.write("in.get_int_array(%s)" % i) else: error ("unsupported parameter for %s, %s" % (ty, name, p)) elif kind == OUT_ARRAY: diff --git a/scripts/update_header_guards.py b/scripts/update_header_guards.py new file mode 100644 index 000000000..11cd6aea2 --- /dev/null +++ b/scripts/update_header_guards.py @@ -0,0 +1,71 @@ +# Copyright (c) 2015 Microsoft Corporation + +import os +import re + +ifndef = re.compile("#ifndef \_(.*)\_H\_") +doubleu = re.compile("#(.*) (.*)\_\_H\_") +defn = re.compile("#define \_(.*)\_H\_") +endif = re.compile("#endif /\* \_(.*)\_H\_") + + +def fix_hdr(file): + print file + tmp = "%s.tmp" % file + ins = open(file) + ous = open(tmp,'w') + line = ins.readline() + found = False + while line: + m = doubleu.search(line) + if m: + ous.write("#") + ous.write(m.group(1)) + ous.write(" ") + ous.write(m.group(2)) + ous.write("_H_\n") + line = ins.readline() + found = True + continue + m = ifndef.search(line) + if m: + print m.group(1) + ous.write("#ifndef ") + ous.write(m.group(1)) + ous.write("_H_\n") + line = ins.readline() + found = True + continue + m = defn.search(line) + if m: + ous.write("#define ") + ous.write(m.group(1)) + ous.write("_H_\n") + line = ins.readline() + found = True + continue + m = endif.search(line) + if m: + ous.write("#endif /* ") + ous.write(m.group(1)) + ous.write("_H_ */\n") + line = ins.readline() + found = True + continue + ous.write(line) + line = ins.readline() + ins.close() + ous.close() + if found: + os.system("move %s %s" % (tmp, file)) + else: + os.system("del %s" % tmp) + +def fixup(dir): + for root, dirs, files in os.walk(dir): + for f in files: + if f.endswith('.h'): + path = "%s\\%s" % (root, f) + fix_hdr(path) + +fixup('src') diff --git a/src/api/api_arith.cpp b/src/api/api_arith.cpp index 55aef4868..dcd250c98 100644 --- a/src/api/api_arith.cpp +++ b/src/api/api_arith.cpp @@ -47,7 +47,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_real(__in Z3_context c, int num, int den) { + Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den) { Z3_TRY; LOG_Z3_mk_real(c, num, den); RESET_ERROR_CODE(); diff --git a/src/api/api_array.cpp b/src/api/api_array.cpp index 415c8696a..de4d6f4e6 100644 --- a/src/api/api_array.cpp +++ b/src/api/api_array.cpp @@ -83,7 +83,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_map(__in Z3_context c, __in Z3_func_decl f, unsigned n, __in Z3_ast const* args) { + Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args) { Z3_TRY; LOG_Z3_mk_map(c, f, n, args); RESET_ERROR_CODE(); @@ -108,7 +108,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_const_array(__in Z3_context c, __in Z3_sort domain, __in Z3_ast v) { + Z3_ast Z3_API Z3_mk_const_array(Z3_context c, Z3_sort domain, Z3_ast v) { Z3_TRY; LOG_Z3_mk_const_array(c, domain, v); RESET_ERROR_CODE(); @@ -127,7 +127,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_array_default(__in Z3_context c, __in Z3_ast array) { + Z3_ast Z3_API Z3_mk_array_default(Z3_context c, Z3_ast array) { Z3_TRY; LOG_Z3_mk_array_default(c, array); RESET_ERROR_CODE(); @@ -142,7 +142,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_ast mk_app_array_core(__in Z3_context c, __in Z3_sort domain, __in Z3_ast v) { + Z3_ast mk_app_array_core(Z3_context c, Z3_sort domain, Z3_ast v) { RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _v = to_expr(v); @@ -158,13 +158,13 @@ extern "C" { return of_ast(r); } - Z3_sort Z3_API Z3_mk_set_sort(__in Z3_context c, __in Z3_sort ty) { + Z3_sort Z3_API Z3_mk_set_sort(Z3_context c, Z3_sort ty) { Z3_TRY; return Z3_mk_array_sort(c, ty, Z3_mk_bool_sort(c)); Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_empty_set(__in Z3_context c, __in Z3_sort domain) { + Z3_ast Z3_API Z3_mk_empty_set(Z3_context c, Z3_sort domain) { Z3_TRY; LOG_Z3_mk_empty_set(c, domain); RESET_ERROR_CODE(); @@ -173,7 +173,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_full_set(__in Z3_context c, __in Z3_sort domain) { + Z3_ast Z3_API Z3_mk_full_set(Z3_context c, Z3_sort domain) { Z3_TRY; LOG_Z3_mk_full_set(c, domain); RESET_ERROR_CODE(); @@ -188,15 +188,15 @@ extern "C" { MK_UNARY(Z3_mk_set_complement, mk_c(c)->get_array_fid(), OP_SET_COMPLEMENT, SKIP); MK_BINARY(Z3_mk_set_subset, mk_c(c)->get_array_fid(), OP_SET_SUBSET, SKIP); - Z3_ast Z3_mk_set_member(__in Z3_context c, __in Z3_ast elem, __in Z3_ast set) { + Z3_ast Z3_mk_set_member(Z3_context c, Z3_ast elem, Z3_ast set) { return Z3_mk_select(c, set, elem); } - Z3_ast Z3_mk_set_add(__in Z3_context c, __in Z3_ast set, __in Z3_ast elem) { + Z3_ast Z3_mk_set_add(Z3_context c, Z3_ast set, Z3_ast elem) { return Z3_mk_store(c, set, elem, Z3_mk_true(c)); } - Z3_ast Z3_mk_set_del(__in Z3_context c, __in Z3_ast set, __in Z3_ast elem) { + Z3_ast Z3_mk_set_del(Z3_context c, Z3_ast set, Z3_ast elem) { return Z3_mk_store(c, set, elem, Z3_mk_false(c)); } diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 20ef9ba7f..15a9f5bae 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -24,6 +24,7 @@ Revision History: #include"bv_decl_plugin.h" #include"datatype_decl_plugin.h" #include"array_decl_plugin.h" +#include"pb_decl_plugin.h" #include"ast_translation.h" #include"ast_pp.h" #include"ast_ll_pp.h" @@ -663,12 +664,9 @@ extern "C" { Z3_TRY; LOG_Z3_get_bool_value(c, a); RESET_ERROR_CODE(); + CHECK_IS_EXPR(a, Z3_L_UNDEF); ast_manager & m = mk_c(c)->m(); ast * n = to_ast(a); - if (!is_expr(n)) { - SET_ERROR_CODE(Z3_INVALID_ARG); - return Z3_L_UNDEF; - } if (m.is_true(to_expr(n))) return Z3_L_TRUE; if (m.is_false(to_expr(n))) @@ -1081,7 +1079,6 @@ extern "C" { case OP_BSREM_I: case OP_BUREM_I: case OP_BSMOD_I: - return Z3_OP_UNINTERPRETED; default: UNREACHABLE(); @@ -1090,9 +1087,10 @@ extern "C" { } if (mk_c(c)->get_dt_fid() == _d->get_family_id()) { switch(_d->get_decl_kind()) { - case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR; - case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER; - case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR; + case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR; + case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER; + case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR; + case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD; default: UNREACHABLE(); return Z3_OP_UNINTERPRETED; @@ -1186,6 +1184,15 @@ extern "C" { } } + if (mk_c(c)->get_pb_fid() == _d->get_family_id()) { + switch(_d->get_decl_kind()) { + case OP_PB_LE: return Z3_OP_PB_LE; + case OP_PB_GE: return Z3_OP_PB_GE; + case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST; + default: UNREACHABLE(); + } + } + return Z3_OP_UNINTERPRETED; Z3_CATCH_RETURN(Z3_OP_UNINTERPRETED); } diff --git a/src/api/api_ast_map.h b/src/api/api_ast_map.h index 54bf3432c..8e4f24623 100644 --- a/src/api/api_ast_map.h +++ b/src/api/api_ast_map.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _API_AST_MAP_H_ -#define _API_AST_MAP_H_ +#ifndef API_AST_MAP_H_ +#define API_AST_MAP_H_ #include"api_util.h" #include"obj_hashtable.h" diff --git a/src/api/api_ast_vector.h b/src/api/api_ast_vector.h index 5071390a3..20efa21c5 100644 --- a/src/api/api_ast_vector.h +++ b/src/api/api_ast_vector.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _API_AST_VECTOR_H_ -#define _API_AST_VECTOR_H_ +#ifndef API_AST_VECTOR_H_ +#define API_AST_VECTOR_H_ #include"api_util.h" diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 0109d6e6b..19430592e 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -159,7 +159,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ This function is a shorthand for shl(1, N-1) where N are the number of bits of \c s. */ - Z3_ast Z3_API Z3_mk_bvmsb(__in Z3_context c, __in Z3_sort s) { + Z3_ast Z3_API Z3_mk_bvmsb(Z3_context c, Z3_sort s) { Z3_TRY; RESET_ERROR_CODE(); // Not logging this one, since it is just syntax sugar. @@ -179,19 +179,19 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_CATCH_RETURN(0); } - Z3_ast Z3_mk_bvsmin(__in Z3_context c, __in Z3_sort s) { + Z3_ast Z3_mk_bvsmin(Z3_context c, Z3_sort s) { return Z3_mk_bvmsb(c, s); } - Z3_ast Z3_mk_bvsmax(__in Z3_context c, __in Z3_sort s) { + Z3_ast Z3_mk_bvsmax(Z3_context c, Z3_sort s) { return Z3_mk_bvnot(c, Z3_mk_bvmsb(c, s)); } - Z3_ast Z3_mk_bvumax(__in Z3_context c, __in Z3_sort s) { + Z3_ast Z3_mk_bvumax(Z3_context c, Z3_sort s) { return Z3_mk_int(c, -1, s); } - Z3_ast Z3_API Z3_mk_bvadd_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2, Z3_bool is_signed) { + Z3_ast Z3_API Z3_mk_bvadd_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { Z3_TRY; RESET_ERROR_CODE(); if (is_signed) { @@ -235,7 +235,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ } // only for signed machine integers - Z3_ast Z3_API Z3_mk_bvadd_no_underflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2) { + Z3_ast Z3_API Z3_mk_bvadd_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast zero = Z3_mk_int(c, 0, Z3_get_sort(c, t1)); @@ -263,7 +263,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ } // only for signed machine integers - Z3_ast Z3_API Z3_mk_bvsub_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2) { + Z3_ast Z3_API Z3_mk_bvsub_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast minus_t2 = Z3_mk_bvneg(c, t2); @@ -291,7 +291,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_bvsub_no_underflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2, Z3_bool is_signed) { + Z3_ast Z3_API Z3_mk_bvsub_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { Z3_TRY; RESET_ERROR_CODE(); if (is_signed) { @@ -316,7 +316,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_bvmul_no_overflow(__in Z3_context c, __in Z3_ast n1, __in Z3_ast n2, Z3_bool is_signed) { + Z3_ast Z3_API Z3_mk_bvmul_no_overflow(Z3_context c, Z3_ast n1, Z3_ast n2, Z3_bool is_signed) { LOG_Z3_mk_bvmul_no_overflow(c, n1, n2, is_signed); RESET_ERROR_CODE(); if (is_signed) { @@ -328,13 +328,13 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ } // only for signed machine integers - Z3_ast Z3_API Z3_mk_bvmul_no_underflow(__in Z3_context c, __in Z3_ast n1, __in Z3_ast n2) { + Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast n1, Z3_ast n2) { LOG_Z3_mk_bvmul_no_underflow(c, n1, n2); MK_BINARY_BODY(Z3_mk_bvmul_no_underflow, mk_c(c)->get_bv_fid(), OP_BSMUL_NO_UDFL, SKIP); } // only for signed machine integers - Z3_ast Z3_API Z3_mk_bvneg_no_overflow(__in Z3_context c, __in Z3_ast t) { + Z3_ast Z3_API Z3_mk_bvneg_no_overflow(Z3_context c, Z3_ast t) { Z3_TRY; RESET_ERROR_CODE(); Z3_ast min = Z3_mk_bvsmin(c, Z3_get_sort(c, t)); @@ -346,7 +346,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ } // only for signed machine integers - Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2) { + Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2) { Z3_TRY; RESET_ERROR_CODE(); Z3_sort s = Z3_get_sort(c, t1); diff --git a/src/api/api_config_params.cpp b/src/api/api_config_params.cpp index c46e7363d..5b385a872 100644 --- a/src/api/api_config_params.cpp +++ b/src/api/api_config_params.cpp @@ -37,9 +37,7 @@ extern "C" { catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. - std::ostringstream buffer; - buffer << "Error setting " << param_id << ", " << ex.msg(); - warning_msg(buffer.str().c_str()); + warning_msg(ex.msg()); } } @@ -64,9 +62,7 @@ extern "C" { catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. - std::ostringstream buffer; - buffer << "Error setting " << param_id << ": " << ex.msg(); - warning_msg(buffer.str().c_str()); + warning_msg(ex.msg()); return Z3_FALSE; } } @@ -92,9 +88,7 @@ extern "C" { catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. - std::ostringstream buffer; - buffer << "Error setting " << param_id << ": " << ex.msg(); - warning_msg(buffer.str().c_str()); + warning_msg(ex.msg()); } } diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index cff59cd80..f17a296fa 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -89,7 +89,7 @@ namespace api { m_bv_util(m()), m_datalog_util(m()), m_fpa_util(m()), - m_dtutil(m()), + m_dtutil(m()), m_last_result(m()), m_ast_trail(m()), m_replay_stack() { @@ -111,6 +111,7 @@ namespace api { m_basic_fid = m().get_basic_family_id(); m_arith_fid = m().mk_family_id("arith"); m_bv_fid = m().mk_family_id("bv"); + m_pb_fid = m().mk_family_id("pb"); m_array_fid = m().mk_family_id("array"); m_dt_fid = m().mk_family_id("datatype"); m_datalog_fid = m().mk_family_id("datalog_relation"); @@ -515,6 +516,11 @@ extern "C" { memory::initialize(0); } + void Z3_API Z3_finalize_memory(void) { + LOG_Z3_finalize_memory(); + memory::finalize(); + } + Z3_error_code Z3_API Z3_get_error_code(Z3_context c) { LOG_Z3_get_error_code(c); return mk_c(c)->get_error_code(); @@ -526,7 +532,7 @@ extern "C" { // [Leo]: using exception handling, we don't need global error handlers anymore } - void Z3_API Z3_set_error(__in Z3_context c, __in Z3_error_code e) { + void Z3_API Z3_set_error(Z3_context c, Z3_error_code e) { SET_ERROR_CODE(e); } @@ -578,7 +584,7 @@ extern "C" { }; -Z3_API ast_manager& Z3_get_manager(__in Z3_context c) { +Z3_API ast_manager& Z3_get_manager(Z3_context c) { return mk_c(c)->m(); } diff --git a/src/api/api_context.h b/src/api/api_context.h index f02dc6f16..3d5f0c2bf 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _API_CONTEXT_H_ -#define _API_CONTEXT_H_ +#ifndef API_CONTEXT_H_ +#define API_CONTEXT_H_ #include"z3.h" #include"ast.h" @@ -78,6 +78,7 @@ namespace api { family_id m_bv_fid; family_id m_dt_fid; family_id m_datalog_fid; + family_id m_pb_fid; family_id m_fpa_fid; datatype_decl_plugin * m_dt_plugin; @@ -116,6 +117,7 @@ namespace api { bool produce_unsat_cores() const { return m_params.m_unsat_core; } bool use_auto_config() const { return m_params.m_auto_config; } unsigned get_timeout() const { return m_params.m_timeout; } + unsigned get_rlimit() const { return m_params.m_rlimit; } arith_util & autil() { return m_arith_util; } bv_util & bvutil() { return m_bv_util; } datalog::dl_decl_util & datalog_util() { return m_datalog_util; } @@ -127,6 +129,7 @@ namespace api { family_id get_bv_fid() const { return m_bv_fid; } family_id get_dt_fid() const { return m_dt_fid; } family_id get_datalog_fid() const { return m_datalog_fid; } + family_id get_pb_fid() const { return m_pb_fid; } family_id get_fpa_fid() const { return m_fpa_fid; } datatype_decl_plugin * get_dt_plugin() const { return m_dt_plugin; } @@ -242,6 +245,7 @@ inline api::context * mk_c(Z3_context c) { return reinterpret_castcheck_searching(); inline bool is_expr(Z3_ast a) { return is_expr(to_ast(a)); } +#define CHECK_IS_EXPR(_p_, _ret_) { if (!is_expr(_p_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } inline bool is_bool_expr(Z3_context c, Z3_ast a) { return is_expr(a) && mk_c(c)->m().is_bool(to_expr(a)); } #define CHECK_FORMULA(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_) || !is_bool_expr(c, _a_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } inline void check_sorts(Z3_context c, ast * n) { mk_c(c)->check_sorts(n); } diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index ad6b5ad69..c3fdeb94b 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -125,7 +125,7 @@ namespace api { return "unknown"; } } - std::string to_string(unsigned num_queries, expr*const* queries) { + std::string to_string(unsigned num_queries, expr* const* queries) { std::stringstream str; m_context.display_smt2(num_queries, queries, str); return str.str(); @@ -287,9 +287,11 @@ extern "C" { lbool r = l_undef; cancel_eh eh(*to_fixedpoint_ref(d)); unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); + unsigned rlimit = to_fixedpoint(d)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); + scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); try { r = to_fixedpoint_ref(d)->ctx().query(to_expr(q)); } @@ -304,8 +306,8 @@ extern "C" { } Z3_lbool Z3_API Z3_fixedpoint_query_relations( - __in Z3_context c,__in Z3_fixedpoint d, - __in unsigned num_relations, Z3_func_decl const relations[]) { + Z3_context c,Z3_fixedpoint d, + unsigned num_relations, Z3_func_decl const relations[]) { Z3_TRY; LOG_Z3_fixedpoint_query_relations(c, d, num_relations, relations); RESET_ERROR_CODE(); @@ -466,13 +468,16 @@ extern "C" { ast_manager& m = mk_c(c)->m(); Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, m); mk_c(c)->save_object(v); - expr_ref_vector rules(m); + expr_ref_vector rules(m), queries(m); svector names; - to_fixedpoint_ref(d)->ctx().get_rules_as_formulas(rules, names); + to_fixedpoint_ref(d)->ctx().get_rules_as_formulas(rules, queries, names); for (unsigned i = 0; i < rules.size(); ++i) { v->m_ast_vector.push_back(rules[i].get()); } + for (unsigned i = 0; i < queries.size(); ++i) { + v->m_ast_vector.push_back(m.mk_not(queries[i].get())); + } RETURN_Z3(of_ast_vector(v)); Z3_CATCH_RETURN(0); } diff --git a/src/api/api_datalog.h b/src/api/api_datalog.h index f5bfada33..8317426c1 100644 --- a/src/api/api_datalog.h +++ b/src/api/api_datalog.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _API_DATALOG_H_ -#define _API_DATALOG_H_ +#ifndef API_DATALOG_H_ +#define API_DATALOG_H_ #include"z3.h" #include"ast.h" diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp index a9794c980..706ba9d89 100644 --- a/src/api/api_datatype.cpp +++ b/src/api/api_datatype.cpp @@ -476,7 +476,7 @@ extern "C" { return 0; } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); - if (idx >= decls->size()) { + if (!decls || idx >= decls->size()) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } @@ -506,9 +506,9 @@ extern "C" { RETURN_Z3(0); } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); - if (idx >= decls->size()) { + if (!decls || idx >= decls->size()) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + RETURN_Z3(0); } func_decl* decl = (*decls)[idx]; decl = dt_util.get_constructor_recognizer(decl); @@ -529,7 +529,7 @@ extern "C" { RETURN_Z3(0); } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); - if (idx_c >= decls->size()) { + if (!decls || idx_c >= decls->size()) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } @@ -618,4 +618,25 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_ast Z3_datatype_update_field( + Z3_context c, Z3_func_decl f, Z3_ast t, Z3_ast v) { + Z3_TRY; + LOG_Z3_datatype_update_field(c, f, t, v); + RESET_ERROR_CODE(); + ast_manager & m = mk_c(c)->m(); + func_decl* _f = to_func_decl(f); + expr* _t = to_expr(t); + expr* _v = to_expr(v); + expr* args[2] = { _t, _v }; + sort* domain[2] = { m.get_sort(_t), m.get_sort(_v) }; + parameter param(_f); + func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_DT_UPDATE_FIELD, 1, ¶m, 2, domain); + app* r = m.mk_app(d, 2, args); + mk_c(c)->save_ast_trail(r); + check_sorts(c, r); + RETURN_Z3(of_ast(r)); + Z3_CATCH_RETURN(0); + } + + }; diff --git a/src/api/api_fpa.cpp b/src/api/api_fpa.cpp index e7ae685fc..290f27ac5 100644 --- a/src/api/api_fpa.cpp +++ b/src/api/api_fpa.cpp @@ -712,7 +712,7 @@ extern "C" { unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s) { Z3_TRY; - LOG_Z3_fpa_get_ebits(c, s); + LOG_Z3_fpa_get_sbits(c, s); RESET_ERROR_CODE(); CHECK_NON_NULL(s, 0); return mk_c(c)->fpautil().get_sbits(to_sort(s)); @@ -737,7 +737,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_string Z3_API Z3_fpa_get_numeral_significand_string(__in Z3_context c, __in Z3_ast t) { + Z3_string Z3_API Z3_fpa_get_numeral_significand_string(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_string(c, t); RESET_ERROR_CODE(); @@ -765,10 +765,33 @@ extern "C" { mpqm.display_decimal(ss, q, sbits); return mk_c(c)->mk_external_string(ss.str()); Z3_CATCH_RETURN(""); - } - Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(__in Z3_context c, __in Z3_ast t) { + Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, __uint64 * n) { + Z3_TRY; + LOG_Z3_fpa_get_numeral_significand_uint64(c, t, n); + RESET_ERROR_CODE(); + ast_manager & m = mk_c(c)->m(); + mpf_manager & mpfm = mk_c(c)->fpautil().fm(); + unsynch_mpz_manager & mpzm = mpfm.mpz_manager(); + fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); + scoped_mpf val(mpfm); + bool r = plugin->is_numeral(to_expr(t), val); + if (!r) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } + const mpz & z = mpfm.sig(val); + if (!mpzm.is_uint64(z)) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } + *n = mpzm.get_uint64(z); + return 1; + Z3_CATCH_RETURN(0); + } + + Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_string(c, t); RESET_ERROR_CODE(); @@ -792,9 +815,9 @@ extern "C" { Z3_CATCH_RETURN(""); } - Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(__in Z3_context c, __in Z3_ast t, __out __int64 * n) { + Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n) { Z3_TRY; - LOG_Z3_fpa_get_numeral_exponent_string(c, t); + LOG_Z3_fpa_get_numeral_exponent_int64(c, t, n); RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); @@ -815,7 +838,7 @@ extern "C" { LOG_Z3_mk_fpa_to_ieee_bv(c, t); RESET_ERROR_CODE(); api::context * ctx = mk_c(c); - Z3_ast r = of_ast(ctx->fpautil().mk_float_to_ieee_bv(to_expr(t))); + Z3_ast r = of_ast(ctx->fpautil().mk_to_ieee_bv(to_expr(t))); RETURN_Z3(r); Z3_CATCH_RETURN(0); } diff --git a/src/api/api_goal.h b/src/api/api_goal.h index 50baf451e..c3d4d44f2 100644 --- a/src/api/api_goal.h +++ b/src/api/api_goal.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _API_GOAL_H_ -#define _API_GOAL_H_ +#ifndef API_GOAL_H_ +#define API_GOAL_H_ #include"api_util.h" #include"goal.h" diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 615b798bb..fab778bb3 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -207,7 +207,7 @@ extern "C" { opts->map[name] = value; } - Z3_ast_vector Z3_API Z3_get_interpolant(__in Z3_context c, __in Z3_ast pf, __in Z3_ast pat, __in Z3_params p){ + Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p){ Z3_TRY; LOG_Z3_get_interpolant(c, pf, pat, p); RESET_ERROR_CODE(); @@ -240,7 +240,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, __in Z3_ast pat, __in Z3_params p, __out Z3_ast_vector *out_interp, __out Z3_model *model){ + Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, Z3_ast pat, Z3_params p, Z3_ast_vector *out_interp, Z3_model *model){ Z3_TRY; LOG_Z3_compute_interpolant(c, pat, p, out_interp, model); RESET_ERROR_CODE(); @@ -290,10 +290,10 @@ extern "C" { } } else { - model_ref _m; - m_solver.get()->get_model(_m); + model_ref mr; + m_solver.get()->get_model(mr); Z3_model_ref *tmp_val = alloc(Z3_model_ref); - tmp_val->m_model = _m.get(); + tmp_val->m_model = mr.get(); mk_c(c)->save_object(tmp_val); *model = of_model(tmp_val); } @@ -708,15 +708,15 @@ extern "C" { def_API('Z3_interpolate', BOOL, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(PARAMS), _out_array(1, AST), _out(MODEL), _out(LITERALS), _in(UINT), _in(UINT), _in_array(9, AST))) */ -Z3_lbool Z3_API Z3_interpolate(__in Z3_context ctx, - __in unsigned num, - __in_ecount(num) Z3_ast *cnsts, - __in_ecount(num) unsigned *parents, - __in Z3_params options, - __out_ecount(num - 1) Z3_ast *interps, - __out Z3_model *model, - __out Z3_literals *labels, - __in unsigned incremental, - __in unsigned num_theory, - __in_ecount(num_theory) Z3_ast *theory); +Z3_lbool Z3_API Z3_interpolate(Z3_context ctx, + unsigned num, + Z3_ast *cnsts, + unsigned *parents, + Z3_params options, + Z3_ast *interps, + Z3_model *model, + Z3_literals *labels, + unsigned incremental, + unsigned num_theory, + Z3_ast *theory); #endif diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 5ae36532b..633d51fab 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -153,6 +153,7 @@ extern "C" { if (v) *v = 0; RESET_ERROR_CODE(); CHECK_NON_NULL(m, Z3_FALSE); + CHECK_IS_EXPR(t, Z3_FALSE); model * _m = to_model_ref(m); expr_ref result(mk_c(c)->m()); _m->eval(to_expr(t), result, model_completion == Z3_TRUE); diff --git a/src/api/api_model.h b/src/api/api_model.h index 454bd48cb..719326aaf 100644 --- a/src/api/api_model.h +++ b/src/api/api_model.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _API_MODEL_H_ -#define _API_MODEL_H_ +#ifndef API_MODEL_H_ +#define API_MODEL_H_ #include"api_util.h" #include"model.h" diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp new file mode 100644 index 000000000..0f322c6e9 --- /dev/null +++ b/src/api/api_opt.cpp @@ -0,0 +1,251 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + api_opt.cpp + +Abstract: + API for optimization + +Author: + + Nikolaj Bjorner (nbjorner) 2013-12-3. + +Revision History: + +--*/ +#include +#include"z3.h" +#include"api_log_macros.h" +#include"api_stats.h" +#include"api_context.h" +#include"api_util.h" +#include"api_model.h" +#include"opt_context.h" +#include"cancel_eh.h" +#include"scoped_timer.h" + + +extern "C" { + + struct Z3_optimize_ref : public api::object { + opt::context* m_opt; + Z3_optimize_ref():m_opt(0) {} + virtual ~Z3_optimize_ref() { dealloc(m_opt); } + }; + inline Z3_optimize_ref * to_optimize(Z3_optimize o) { return reinterpret_cast(o); } + inline Z3_optimize of_optimize(Z3_optimize_ref * o) { return reinterpret_cast(o); } + inline opt::context* to_optimize_ptr(Z3_optimize o) { return to_optimize(o)->m_opt; } + + Z3_optimize Z3_API Z3_mk_optimize(Z3_context c) { + Z3_TRY; + LOG_Z3_mk_optimize(c); + RESET_ERROR_CODE(); + Z3_optimize_ref * o = alloc(Z3_optimize_ref); + o->m_opt = alloc(opt::context,mk_c(c)->m()); + mk_c(c)->save_object(o); + RETURN_Z3(of_optimize(o)); + Z3_CATCH_RETURN(0); + } + + void Z3_API Z3_optimize_inc_ref(Z3_context c, Z3_optimize o) { + Z3_TRY; + LOG_Z3_optimize_inc_ref(c, o); + RESET_ERROR_CODE(); + to_optimize(o)->inc_ref(); + Z3_CATCH; + } + + void Z3_API Z3_optimize_dec_ref(Z3_context c, Z3_optimize o) { + Z3_TRY; + LOG_Z3_optimize_dec_ref(c, o); + RESET_ERROR_CODE(); + to_optimize(o)->dec_ref(); + Z3_CATCH; + } + + void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a) { + Z3_TRY; + LOG_Z3_optimize_assert(c, o, a); + RESET_ERROR_CODE(); + CHECK_FORMULA(a,); + to_optimize_ptr(o)->add_hard_constraint(to_expr(a)); + Z3_CATCH; + } + + unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id) { + Z3_TRY; + LOG_Z3_optimize_assert_soft(c, o, a, weight, id); + RESET_ERROR_CODE(); + CHECK_FORMULA(a,0); + rational w(weight); + return to_optimize_ptr(o)->add_soft_constraint(to_expr(a), w, to_symbol(id)); + Z3_CATCH_RETURN(0); + } + + unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t) { + Z3_TRY; + LOG_Z3_optimize_maximize(c, o, t); + RESET_ERROR_CODE(); + CHECK_VALID_AST(t,0); + return to_optimize_ptr(o)->add_objective(to_app(t), true); + Z3_CATCH_RETURN(0); + } + + unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t) { + Z3_TRY; + LOG_Z3_optimize_minimize(c, o, t); + RESET_ERROR_CODE(); + CHECK_VALID_AST(t,0); + return to_optimize_ptr(o)->add_objective(to_app(t), false); + Z3_CATCH_RETURN(0); + } + + void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d) { + Z3_TRY; + LOG_Z3_optimize_push(c, d); + RESET_ERROR_CODE(); + to_optimize_ptr(d)->push(); + Z3_CATCH; + } + + void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d) { + Z3_TRY; + LOG_Z3_optimize_pop(c, d); + RESET_ERROR_CODE(); + to_optimize_ptr(d)->pop(1); + Z3_CATCH; + } + + + Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o) { + Z3_TRY; + LOG_Z3_optimize_check(c, o); + RESET_ERROR_CODE(); + lbool r = l_undef; + cancel_eh eh(*to_optimize_ptr(o)); + unsigned timeout = to_optimize_ptr(o)->get_params().get_uint("timeout", mk_c(c)->get_timeout()); + api::context::set_interruptable si(*(mk_c(c)), eh); + { + scoped_timer timer(timeout, &eh); + try { + r = to_optimize_ptr(o)->optimize(); + } + catch (z3_exception& ex) { + mk_c(c)->handle_exception(ex); + r = l_undef; + } + // to_optimize_ref(d).cleanup(); + } + return of_lbool(r); + Z3_CATCH_RETURN(Z3_L_UNDEF); + } + + Z3_string Z3_API Z3_optimize_get_reason_unknown(Z3_context c, Z3_optimize o) { + Z3_TRY; + LOG_Z3_optimize_to_string(c, o); + RESET_ERROR_CODE(); + return mk_c(c)->mk_external_string(to_optimize_ptr(o)->reason_unknown()); + Z3_CATCH_RETURN(""); + } + + Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o) { + Z3_TRY; + LOG_Z3_optimize_get_model(c, o); + RESET_ERROR_CODE(); + model_ref _m; + to_optimize_ptr(o)->get_model(_m); + Z3_model_ref * m_ref = alloc(Z3_model_ref); + if (_m) { + m_ref->m_model = _m; + } + else { + m_ref->m_model = alloc(model, mk_c(c)->m()); + } + mk_c(c)->save_object(m_ref); + RETURN_Z3(of_model(m_ref)); + Z3_CATCH_RETURN(0); + } + + void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p) { + Z3_TRY; + LOG_Z3_optimize_set_params(c, o, p); + RESET_ERROR_CODE(); + param_descrs descrs; + to_optimize_ptr(o)->collect_param_descrs(descrs); + to_params(p)->m_params.validate(descrs); + params_ref pr = to_param_ref(p); + to_optimize_ptr(o)->updt_params(pr); + Z3_CATCH; + } + + Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o) { + Z3_TRY; + LOG_Z3_optimize_get_param_descrs(c, o); + RESET_ERROR_CODE(); + Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); + mk_c(c)->save_object(d); + to_optimize_ptr(o)->collect_param_descrs(d->m_descrs); + Z3_param_descrs r = of_param_descrs(d); + RETURN_Z3(r); + Z3_CATCH_RETURN(0); + } + + // get lower value or current approximation + Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx) { + Z3_TRY; + LOG_Z3_optimize_get_lower(c, o, idx); + RESET_ERROR_CODE(); + expr_ref e = to_optimize_ptr(o)->get_lower(idx); + mk_c(c)->save_ast_trail(e); + RETURN_Z3(of_expr(e)); + Z3_CATCH_RETURN(0); + } + + // get upper or current approximation + Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx) { + Z3_TRY; + LOG_Z3_optimize_get_upper(c, o, idx); + RESET_ERROR_CODE(); + expr_ref e = to_optimize_ptr(o)->get_upper(idx); + mk_c(c)->save_ast_trail(e); + RETURN_Z3(of_expr(e)); + Z3_CATCH_RETURN(0); + } + + Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o) { + Z3_TRY; + LOG_Z3_optimize_to_string(c, o); + RESET_ERROR_CODE(); + return mk_c(c)->mk_external_string(to_optimize_ptr(o)->to_string()); + Z3_CATCH_RETURN(""); + } + + Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize d) { + Z3_TRY; + LOG_Z3_optimize_get_help(c, d); + RESET_ERROR_CODE(); + std::ostringstream buffer; + param_descrs descrs; + to_optimize_ptr(d)->collect_param_descrs(descrs); + descrs.display(buffer); + return mk_c(c)->mk_external_string(buffer.str()); + Z3_CATCH_RETURN(""); + } + + Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c,Z3_optimize d) { + Z3_TRY; + LOG_Z3_optimize_get_statistics(c, d); + RESET_ERROR_CODE(); + Z3_stats_ref * st = alloc(Z3_stats_ref); + to_optimize_ptr(d)->collect_statistics(st->m_stats); + mk_c(c)->save_object(st); + Z3_stats r = of_stats(st); + RETURN_Z3(r); + Z3_CATCH_RETURN(0); + } + + + +}; diff --git a/src/api/api_pb.cpp b/src/api/api_pb.cpp new file mode 100644 index 000000000..6d5a56d2c --- /dev/null +++ b/src/api/api_pb.cpp @@ -0,0 +1,61 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + api_pb.cpp + +Abstract: + API for pb theory + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-13. + +Revision History: + +--*/ +#include +#include"z3.h" +#include"api_log_macros.h" +#include"api_context.h" +#include"api_util.h" +#include"pb_decl_plugin.h" + +extern "C" { + + Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args, + Z3_ast const args[], unsigned k) { + Z3_TRY; + LOG_Z3_mk_atmost(c, num_args, args, k); + RESET_ERROR_CODE(); + parameter param(k); + pb_util util(mk_c(c)->m()); + ast* a = util.mk_at_most_k(num_args, to_exprs(args), k); + mk_c(c)->save_ast_trail(a); + check_sorts(c, a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(0); + } + + + Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, + Z3_ast const args[], int _coeffs[], + int k) { + Z3_TRY; + LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); + RESET_ERROR_CODE(); + pb_util util(mk_c(c)->m()); + vector coeffs; + for (unsigned i = 0; i < num_args; ++i) { + coeffs.push_back(rational(_coeffs[i])); + } + ast* a = util.mk_le(num_args, coeffs.c_ptr(), to_exprs(args), rational(k)); + mk_c(c)->save_ast_trail(a); + check_sorts(c, a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(0); + } + + +}; diff --git a/src/api/api_polynomial.h b/src/api/api_polynomial.h index 27317a7dd..a5a0bd7c3 100644 --- a/src/api/api_polynomial.h +++ b/src/api/api_polynomial.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _API_POLYNOMIAL_H_ -#define _API_POLYNOMIAL_H_ +#ifndef API_POLYNOMIAL_H_ +#define API_POLYNOMIAL_H_ #include"polynomial.h" diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index e57050e82..4daf3956f 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -254,6 +254,7 @@ extern "C" { } expr * const * _assumptions = to_exprs(assumptions); unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); + unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", false); cancel_eh eh(*to_solver_ref(s)); api::context::set_interruptable si(*(mk_c(c)), eh); @@ -261,6 +262,7 @@ extern "C" { { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); + scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); try { result = to_solver_ref(s)->check_sat(num_assumptions, _assumptions); } @@ -355,6 +357,8 @@ extern "C" { init_solver(c, s); Z3_stats_ref * st = alloc(Z3_stats_ref); to_solver_ref(s)->collect_statistics(st->m_stats); + get_memory_statistics(st->m_stats); + get_rlimit_statistics(mk_c(c)->m().limit(), st->m_stats); mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); diff --git a/src/api/api_solver.h b/src/api/api_solver.h index f11c9bfd8..5ed7a15f7 100644 --- a/src/api/api_solver.h +++ b/src/api/api_solver.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _API_SOLVER_H_ -#define _API_SOLVER_H_ +#ifndef API_SOLVER_H_ +#define API_SOLVER_H_ #include"api_util.h" #include"solver.h" diff --git a/src/api/api_stats.h b/src/api/api_stats.h index 393161309..e0e370336 100644 --- a/src/api/api_stats.h +++ b/src/api/api_stats.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _API_STATS_H_ -#define _API_STATS_H_ +#ifndef API_STATS_H_ +#define API_STATS_H_ #include"api_util.h" #include"statistics.h" diff --git a/src/api/api_tactic.h b/src/api/api_tactic.h index 80b24ff1a..6f8d83fd9 100644 --- a/src/api/api_tactic.h +++ b/src/api/api_tactic.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _API_TACTIC_H_ -#define _API_TACTIC_H_ +#ifndef API_TACTIC_H_ +#define API_TACTIC_H_ #include"api_goal.h" #include"tactical.h" diff --git a/src/api/api_util.h b/src/api/api_util.h index b49ef8315..9befd8849 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _API_UTIL_H_ -#define _API_UTIL_H_ +#ifndef API_UTIL_H_ +#define API_UTIL_H_ #include"params.h" #include"lbool.h" diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index f43ba83e5..a5d943e95 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -18,8 +18,8 @@ Author: Notes: --*/ -#ifndef __Z3PP_H_ -#define __Z3PP_H_ +#ifndef Z3PP_H_ +#define Z3PP_H_ #include #include @@ -203,7 +203,12 @@ namespace z3 { and in \c ts the predicates for testing if terms of the enumeration sort correspond to an enumeration. */ sort enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts); - + /** + \brief create an uninterpreted sort with the name given by the string or symbol. + */ + sort uninterpreted_sort(char const* name); + sort uninterpreted_sort(symbol const& name); + func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl function(symbol const& name, sort_vector const& domain, sort const& range); @@ -655,6 +660,7 @@ namespace z3 { friend expr ite(expr const & c, expr const & t, expr const & e); friend expr distinct(expr_vector const& args); + friend expr concat(expr const& a, expr const& b); friend expr operator==(expr const & a, expr const & b) { check_context(a, b); @@ -677,7 +683,7 @@ namespace z3 { friend expr operator+(expr const & a, expr const & b) { check_context(a, b); - Z3_ast r; + Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_add(a.ctx(), 2, args); @@ -697,7 +703,7 @@ namespace z3 { friend expr operator*(expr const & a, expr const & b) { check_context(a, b); - Z3_ast r; + Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_mul(a.ctx(), 2, args); @@ -724,7 +730,7 @@ namespace z3 { friend expr operator/(expr const & a, expr const & b) { check_context(a, b); - Z3_ast r; + Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_div(a.ctx(), a, b); } @@ -742,7 +748,7 @@ namespace z3 { friend expr operator/(int a, expr const & b) { return b.ctx().num_val(a, b.get_sort()) / b; } friend expr operator-(expr const & a) { - Z3_ast r; + Z3_ast r = 0; if (a.is_arith()) { r = Z3_mk_unary_minus(a.ctx(), a); } @@ -759,7 +765,7 @@ namespace z3 { friend expr operator-(expr const & a, expr const & b) { check_context(a, b); - Z3_ast r; + Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { Z3_ast args[2] = { a, b }; r = Z3_mk_sub(a.ctx(), 2, args); @@ -779,7 +785,7 @@ namespace z3 { friend expr operator<=(expr const & a, expr const & b) { check_context(a, b); - Z3_ast r; + Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_le(a.ctx(), a, b); } @@ -798,7 +804,7 @@ namespace z3 { friend expr operator>=(expr const & a, expr const & b) { check_context(a, b); - Z3_ast r; + Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_ge(a.ctx(), a, b); } @@ -817,7 +823,7 @@ namespace z3 { friend expr operator<(expr const & a, expr const & b) { check_context(a, b); - Z3_ast r; + Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_lt(a.ctx(), a, b); } @@ -836,7 +842,7 @@ namespace z3 { friend expr operator>(expr const & a, expr const & b) { check_context(a, b); - Z3_ast r; + Z3_ast r = 0; if (a.is_arith() && b.is_arith()) { r = Z3_mk_gt(a.ctx(), a, b); } @@ -1108,6 +1114,13 @@ namespace z3 { ctx.check_error(); return expr(ctx, r); } + + inline expr concat(expr const& a, expr const& b) { + check_context(a, b); + Z3_ast r = Z3_mk_concat(a.ctx(), a, b); + a.ctx().check_error(); + return expr(a.ctx(), r); + } class func_entry : public object { Z3_func_entry m_entry; @@ -1176,7 +1189,7 @@ namespace z3 { expr eval(expr const & n, bool model_completion=false) const { check_context(*this, n); - Z3_ast r; + Z3_ast r = 0; Z3_bool status = Z3_model_eval(ctx(), m_model, n, model_completion, &r); check_error(); if (status == Z3_FALSE) @@ -1536,6 +1549,62 @@ namespace z3 { } }; + class optimize : public object { + Z3_optimize m_opt; + public: + class handle { + unsigned m_h; + public: + handle(unsigned h): m_h(h) {} + unsigned h() const { return m_h; } + }; + optimize(context& c):object(c) { m_opt = Z3_mk_optimize(c); Z3_optimize_inc_ref(c, m_opt); } + ~optimize() { Z3_optimize_dec_ref(ctx(), m_opt); } + operator Z3_optimize() const { return m_opt; } + void add(expr const& e) { + assert(e.is_bool()); + Z3_optimize_assert(ctx(), m_opt, e); + } + handle add(expr const& e, unsigned weight) { + assert(e.is_bool()); + std::stringstream strm; + strm << weight; + return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, strm.str().c_str(), 0)); + } + handle add(expr const& e, char const* weight) { + assert(e.is_bool()); + return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, weight, 0)); + } + handle maximize(expr const& e) { + return handle(Z3_optimize_maximize(ctx(), m_opt, e)); + } + handle minimize(expr const& e) { + return handle(Z3_optimize_minimize(ctx(), m_opt, e)); + } + void push() { + Z3_optimize_push(ctx(), m_opt); + } + void pop() { + Z3_optimize_pop(ctx(), m_opt); + } + check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt); check_error(); return to_check_result(r); } + model get_model() const { Z3_model m = Z3_optimize_get_model(ctx(), m_opt); check_error(); return model(ctx(), m); } + void set(params const & p) { Z3_optimize_set_params(ctx(), m_opt, p); check_error(); } + expr lower(handle const& h) { + Z3_ast r = Z3_optimize_get_lower(ctx(), m_opt, h.h()); + check_error(); + return expr(ctx(), r); + } + expr upper(handle const& h) { + Z3_ast r = Z3_optimize_get_upper(ctx(), m_opt, h.h()); + check_error(); + return expr(ctx(), r); + } + stats statistics() const { Z3_stats r = Z3_optimize_get_statistics(ctx(), m_opt); check_error(); return stats(ctx(), r); } + friend std::ostream & operator<<(std::ostream & out, optimize const & s) { out << Z3_optimize_to_string(s.ctx(), s.m_opt); return out; } + std::string help() const { char const * r = Z3_optimize_get_help(ctx(), m_opt); check_error(); return r; } + }; + inline tactic fail_if(probe const & p) { Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); p.check_error(); @@ -1573,6 +1642,13 @@ namespace z3 { for (unsigned i = 0; i < n; i++) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); } return s; } + inline sort context::uninterpreted_sort(char const* name) { + Z3_symbol _name = Z3_mk_string_symbol(*this, name); + return to_sort(*this, Z3_mk_uninterpreted_sort(*this, _name)); + } + inline sort context::uninterpreted_sort(symbol const& name) { + return to_sort(*this, Z3_mk_uninterpreted_sort(*this, name)); + } inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { array args(arity); diff --git a/src/api/dll/dll.cpp b/src/api/dll/dll.cpp index 1730ede23..a0bd25d2e 100644 --- a/src/api/dll/dll.cpp +++ b/src/api/dll/dll.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifdef _WINDOWS #include diff --git a/src/api/dotnet/ASTMap.cs b/src/api/dotnet/ASTMap.cs index 6f296147e..596b2943f 100644 --- a/src/api/dotnet/ASTMap.cs +++ b/src/api/dotnet/ASTMap.cs @@ -98,11 +98,12 @@ namespace Microsoft.Z3 /// /// The keys stored in the map. /// - public ASTVector Keys + public AST[] Keys { get { - return new ASTVector(Context, Native.Z3_ast_map_keys(Context.nCtx, NativeObject)); + ASTVector res = new ASTVector(Context, Native.Z3_ast_map_keys(Context.nCtx, NativeObject)); + return res.ToArray(); } } diff --git a/src/api/dotnet/ASTVector.cs b/src/api/dotnet/ASTVector.cs index b70ff13b6..8b599ca48 100644 --- a/src/api/dotnet/ASTVector.cs +++ b/src/api/dotnet/ASTVector.cs @@ -99,6 +99,138 @@ namespace Microsoft.Z3 return Native.Z3_ast_vector_to_string(Context.nCtx, NativeObject); } + /// + /// Translates an AST vector into an AST[] + /// + public AST[] ToArray() + { + uint n = Size; + AST[] res = new AST[n]; + for (uint i = 0; i < n; i++) + res[i] = AST.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into an Expr[] + /// + public Expr[] ToExprArray() + { + uint n = Size; + Expr[] res = new Expr[n]; + for (uint i = 0; i < n; i++) + res[i] = Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a BoolExpr[] + /// + public BoolExpr[] ToBoolExprArray() + { + uint n = Size; + BoolExpr[] res = new BoolExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (BoolExpr) Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a BitVecExpr[] + /// + public BitVecExpr[] ToBitVecExprArray() + { + uint n = Size; + BitVecExpr[] res = new BitVecExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (BitVecExpr)Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a ArithExpr[] + /// + public ArithExpr[] ToArithExprArray() + { + uint n = Size; + ArithExpr[] res = new ArithExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (ArithExpr)Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a ArrayExpr[] + /// + public ArrayExpr[] ToArrayExprArray() + { + uint n = Size; + ArrayExpr[] res = new ArrayExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (ArrayExpr)Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a DatatypeExpr[] + /// + public DatatypeExpr[] ToDatatypeExprArray() + { + uint n = Size; + DatatypeExpr[] res = new DatatypeExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (DatatypeExpr)Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a FPExpr[] + /// + public FPExpr[] ToFPExprArray() + { + uint n = Size; + FPExpr[] res = new FPExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (FPExpr)Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a FPRMExpr[] + /// + public FPRMExpr[] ToFPRMExprArray() + { + uint n = Size; + FPRMExpr[] res = new FPRMExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (FPRMExpr)Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a IntExpr[] + /// + public IntExpr[] ToIntExprArray() + { + uint n = Size; + IntExpr[] res = new IntExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (IntExpr)Expr.Create(this.Context, this[i].NativeObject); + return res; + } + + /// + /// Translates an ASTVector into a RealExpr[] + /// + public RealExpr[] ToRealExprArray() + { + uint n = Size; + RealExpr[] res = new RealExpr[n]; + for (uint i = 0; i < n; i++) + res[i] = (RealExpr)Expr.Create(this.Context, this[i].NativeObject); + return res; + } + #region Internal internal ASTVector(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal ASTVector(Context ctx) : base(ctx, Native.Z3_mk_ast_vector(ctx.nCtx)) { Contract.Requires(ctx != null); } diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index b4fb954b8..0aee3f7d8 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -449,6 +449,19 @@ namespace Microsoft.Z3 return MkDatatypeSorts(MkSymbols(names), c); } + /// + /// Update a datatype field at expression t with value v. + /// The function performs a record update at t. The field + /// that is passed in as argument is updated with value v, + /// the remainig fields of t are unchanged. + /// + public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) + { + return Expr.Create(this, Native.Z3_datatype_update_field( + nCtx, field.NativeObject, + t.NativeObject, v.NativeObject)); + } + #endregion #endregion @@ -2121,31 +2134,31 @@ namespace Microsoft.Z3 /// /// Create an empty set. /// - public Expr MkEmptySet(Sort domain) + public ArrayExpr MkEmptySet(Sort domain) { Contract.Requires(domain != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); - return Expr.Create(this, Native.Z3_mk_empty_set(nCtx, domain.NativeObject)); + return (ArrayExpr)Expr.Create(this, Native.Z3_mk_empty_set(nCtx, domain.NativeObject)); } /// /// Create the full set. /// - public Expr MkFullSet(Sort domain) + public ArrayExpr MkFullSet(Sort domain) { Contract.Requires(domain != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(domain); - return Expr.Create(this, Native.Z3_mk_full_set(nCtx, domain.NativeObject)); + return (ArrayExpr)Expr.Create(this, Native.Z3_mk_full_set(nCtx, domain.NativeObject)); } /// /// Add an element to the set. /// - public Expr MkSetAdd(Expr set, Expr element) + public ArrayExpr MkSetAdd(ArrayExpr set, Expr element) { Contract.Requires(set != null); Contract.Requires(element != null); @@ -2153,14 +2166,14 @@ namespace Microsoft.Z3 CheckContextMatch(set); CheckContextMatch(element); - return Expr.Create(this, Native.Z3_mk_set_add(nCtx, set.NativeObject, element.NativeObject)); + return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_add(nCtx, set.NativeObject, element.NativeObject)); } /// /// Remove an element from a set. /// - public Expr MkSetDel(Expr set, Expr element) + public ArrayExpr MkSetDel(ArrayExpr set, Expr element) { Contract.Requires(set != null); Contract.Requires(element != null); @@ -2168,38 +2181,38 @@ namespace Microsoft.Z3 CheckContextMatch(set); CheckContextMatch(element); - return Expr.Create(this, Native.Z3_mk_set_del(nCtx, set.NativeObject, element.NativeObject)); + return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_del(nCtx, set.NativeObject, element.NativeObject)); } /// /// Take the union of a list of sets. /// - public Expr MkSetUnion(params Expr[] args) + public ArrayExpr MkSetUnion(params ArrayExpr[] args) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); CheckContextMatch(args); - return Expr.Create(this, Native.Z3_mk_set_union(nCtx, (uint)args.Length, AST.ArrayToNative(args))); + return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_union(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Take the intersection of a list of sets. /// - public Expr MkSetIntersection(params Expr[] args) + public ArrayExpr MkSetIntersection(params ArrayExpr[] args) { Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); Contract.Ensures(Contract.Result() != null); CheckContextMatch(args); - return Expr.Create(this, Native.Z3_mk_set_intersect(nCtx, (uint)args.Length, AST.ArrayToNative(args))); + return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_intersect(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } /// /// Take the difference between two sets. /// - public Expr MkSetDifference(Expr arg1, Expr arg2) + public ArrayExpr MkSetDifference(ArrayExpr arg1, ArrayExpr arg2) { Contract.Requires(arg1 != null); Contract.Requires(arg2 != null); @@ -2207,25 +2220,25 @@ namespace Microsoft.Z3 CheckContextMatch(arg1); CheckContextMatch(arg2); - return Expr.Create(this, Native.Z3_mk_set_difference(nCtx, arg1.NativeObject, arg2.NativeObject)); + return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_difference(nCtx, arg1.NativeObject, arg2.NativeObject)); } /// /// Take the complement of a set. /// - public Expr MkSetComplement(Expr arg) + public ArrayExpr MkSetComplement(ArrayExpr arg) { Contract.Requires(arg != null); Contract.Ensures(Contract.Result() != null); CheckContextMatch(arg); - return Expr.Create(this, Native.Z3_mk_set_complement(nCtx, arg.NativeObject)); + return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_complement(nCtx, arg.NativeObject)); } /// /// Check for set membership. /// - public Expr MkSetMembership(Expr elem, Expr set) + public BoolExpr MkSetMembership(Expr elem, ArrayExpr set) { Contract.Requires(elem != null); Contract.Requires(set != null); @@ -2233,13 +2246,13 @@ namespace Microsoft.Z3 CheckContextMatch(elem); CheckContextMatch(set); - return Expr.Create(this, Native.Z3_mk_set_member(nCtx, elem.NativeObject, set.NativeObject)); + return (BoolExpr) Expr.Create(this, Native.Z3_mk_set_member(nCtx, elem.NativeObject, set.NativeObject)); } /// /// Check for subsetness of sets. /// - public Expr MkSetSubset(Expr arg1, Expr arg2) + public BoolExpr MkSetSubset(ArrayExpr arg1, ArrayExpr arg2) { Contract.Requires(arg1 != null); Contract.Requires(arg2 != null); @@ -2247,7 +2260,37 @@ namespace Microsoft.Z3 CheckContextMatch(arg1); CheckContextMatch(arg2); - return Expr.Create(this, Native.Z3_mk_set_subset(nCtx, arg1.NativeObject, arg2.NativeObject)); + return (BoolExpr) Expr.Create(this, Native.Z3_mk_set_subset(nCtx, arg1.NativeObject, arg2.NativeObject)); + } + #endregion + + #region Pseudo-Boolean constraints + + /// + /// Create an at-most-k constraint. + /// + public BoolExpr MkAtMost(BoolExpr[] args, uint k) + { + Contract.Requires(args != null); + Contract.Requires(Contract.Result() != null); + CheckContextMatch(args); + return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Length, + AST.ArrayToNative(args), k)); + } + + /// + /// Create a pseudo-Boolean less-or-equal constraint. + /// + public BoolExpr MkPBLe(int[] coeffs, BoolExpr[] args, int k) + { + Contract.Requires(args != null); + Contract.Requires(coeffs != null); + Contract.Requires(args.Length == coeffs.Length); + Contract.Requires(Contract.Result() != null); + CheckContextMatch(args); + return new BoolExpr(this, Native.Z3_mk_pble(nCtx, (uint) args.Length, + AST.ArrayToNative(args), + coeffs, k)); } #endregion @@ -3438,6 +3481,18 @@ namespace Microsoft.Z3 } #endregion + #region Optimization + /// + /// Create an Optimization context. + /// + public Optimize MkOptimize() + { + Contract.Ensures(Contract.Result() != null); + + return new Optimize(this); + } + #endregion + #region Floating-Point Arithmetic #region Rounding Modes @@ -4383,6 +4438,7 @@ namespace Microsoft.Z3 Contract.Invariant(m_Statistics_DRQ != null); Contract.Invariant(m_Tactic_DRQ != null); Contract.Invariant(m_Fixedpoint_DRQ != null); + Contract.Invariant(m_Optimize_DRQ != null); } readonly private AST.DecRefQueue m_AST_DRQ = new AST.DecRefQueue(); @@ -4400,6 +4456,7 @@ namespace Microsoft.Z3 readonly private Statistics.DecRefQueue m_Statistics_DRQ = new Statistics.DecRefQueue(10); readonly private Tactic.DecRefQueue m_Tactic_DRQ = new Tactic.DecRefQueue(10); readonly private Fixedpoint.DecRefQueue m_Fixedpoint_DRQ = new Fixedpoint.DecRefQueue(10); + readonly private Optimize.DecRefQueue m_Optimize_DRQ = new Optimize.DecRefQueue(10); /// /// AST DRQ @@ -4476,6 +4533,11 @@ namespace Microsoft.Z3 /// public IDecRefQueue Fixedpoint_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Fixedpoint_DRQ; } } + /// + /// Optimize DRQ + /// + public IDecRefQueue Optimize_DRQ { get { Contract.Ensures(Contract.Result() != null); return m_Fixedpoint_DRQ; } } + internal long refCount = 0; @@ -4518,6 +4580,7 @@ namespace Microsoft.Z3 Statistics_DRQ.Clear(this); Tactic_DRQ.Clear(this); Fixedpoint_DRQ.Clear(this); + Optimize_DRQ.Clear(this); m_boolSort = null; m_intSort = null; diff --git a/src/api/dotnet/Deprecated.cs b/src/api/dotnet/Deprecated.cs new file mode 100644 index 000000000..aa6dffb45 --- /dev/null +++ b/src/api/dotnet/Deprecated.cs @@ -0,0 +1,111 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + Deprecated.cs + +Abstract: + + Expose deprecated features for use from the managed API + those who use them for experiments. + +Author: + + Christoph Wintersteiger (cwinter) 2012-03-15 + +Notes: + +--*/ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Diagnostics.Contracts; + +namespace Microsoft.Z3 +{ + /// + /// The main interaction with Z3 happens via the Context. + /// + [ContractVerification(true)] + public class Deprecated + { + + /// + /// Creates a backtracking point. + /// + /// + public static void Push(Context ctx) { + Native.Z3_push(ctx.nCtx); + } + + /// + /// Backtracks backtracking points. + /// + /// Note that an exception is thrown if is not smaller than NumScopes + /// + public static void Pop(Context ctx, uint n = 1) { + Native.Z3_pop(ctx.nCtx, n); + } + + /// + /// Assert a constraint (or multiple) into the solver. + /// + public static void Assert(Context ctx, params BoolExpr[] constraints) + { + Contract.Requires(constraints != null); + Contract.Requires(Contract.ForAll(constraints, c => c != null)); + + ctx.CheckContextMatch(constraints); + foreach (BoolExpr a in constraints) + { + Native.Z3_assert_cnstr(ctx.nCtx, a.NativeObject); + } + } + /// + /// Checks whether the assertions in the context are consistent or not. + /// + public static Status Check(Context ctx, List core, ref Model model, ref Expr proof, params Expr[] assumptions) + { + Z3_lbool r; + model = null; + proof = null; + if (assumptions == null || assumptions.Length == 0) + r = (Z3_lbool)Native.Z3_check(ctx.nCtx); + else { + IntPtr mdl = IntPtr.Zero, prf = IntPtr.Zero; + uint core_size = 0; + IntPtr[] native_core = new IntPtr[assumptions.Length]; + r = (Z3_lbool)Native.Z3_check_assumptions(ctx.nCtx, + (uint)assumptions.Length, AST.ArrayToNative(assumptions), + ref mdl, ref prf, ref core_size, native_core); + + for (uint i = 0; i < core_size; i++) + core.Add((BoolExpr)Expr.Create(ctx, native_core[i])); + if (mdl != IntPtr.Zero) { + model = new Model(ctx, mdl); + } + if (prf != IntPtr.Zero) { + proof = Expr.Create(ctx, prf); + } + + } + switch (r) + { + case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; + case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; + default: return Status.UNKNOWN; + } + } + + /// + /// Retrieves an assignment to atomic propositions for a satisfiable context. + /// + public static BoolExpr GetAssignment(Context ctx) + { + IntPtr x = Native.Z3_get_context_assignment(ctx.nCtx); + return (BoolExpr)Expr.Create(ctx, x); + } + + } +} \ No newline at end of file diff --git a/src/api/dotnet/FPNum.cs b/src/api/dotnet/FPNum.cs index e85687ccf..ac1fae5f5 100644 --- a/src/api/dotnet/FPNum.cs +++ b/src/api/dotnet/FPNum.cs @@ -59,6 +59,25 @@ namespace Microsoft.Z3 } } + /// + /// The significand value of a floating-point numeral as a UInt64 + /// + /// + /// This function extracts the significand bits, without the + /// hidden bit or normalization. Throws an exception if the + /// significand does not fit into a UInt64. + /// + public UInt64 SignificandUInt64 + { + get + { + UInt64 result = 0; + if (Native.Z3_fpa_get_numeral_significand_uint64(Context.nCtx, NativeObject, ref result) == 0) + throw new Z3Exception("Significand is not a 64 bit unsigned integer"); + return result; + } + } + /// /// Return the exponent value of a floating-point numeral as a string /// diff --git a/src/api/dotnet/Fixedpoint.cs b/src/api/dotnet/Fixedpoint.cs index 281ed9ad2..66449ddbb 100644 --- a/src/api/dotnet/Fixedpoint.cs +++ b/src/api/dotnet/Fixedpoint.cs @@ -269,14 +269,6 @@ namespace Microsoft.Z3 AST.ArrayLength(queries), AST.ArrayToNative(queries)); } - BoolExpr[] ToBoolExprs(ASTVector v) { - uint n = v.Size; - BoolExpr[] res = new BoolExpr[n]; - for (uint i = 0; i < n; i++) - res[i] = new BoolExpr(Context, v[i].NativeObject); - return res; - } - /// /// Retrieve set of rules added to fixedpoint context. /// @@ -286,7 +278,8 @@ namespace Microsoft.Z3 { Contract.Ensures(Contract.Result() != null); - return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject))); + ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_rules(Context.nCtx, NativeObject)); + return av.ToBoolExprArray(); } } @@ -299,7 +292,21 @@ namespace Microsoft.Z3 { Contract.Ensures(Contract.Result() != null); - return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject))); + ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_get_assertions(Context.nCtx, NativeObject)); + return av.ToBoolExprArray(); + } + } + + /// + /// Fixedpoint statistics. + /// + public Statistics Statistics + { + get + { + Contract.Ensures(Contract.Result() != null); + + return new Statistics(Context, Native.Z3_fixedpoint_get_statistics(Context.nCtx, NativeObject)); } } @@ -308,16 +315,19 @@ namespace Microsoft.Z3 /// Add the rules to the current fixedpoint context. /// Return the set of queries in the file. ///
- public BoolExpr[] ParseFile(string file) { - return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_from_file(Context.nCtx, NativeObject, file))); + public BoolExpr[] ParseFile(string file) + { + ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_file(Context.nCtx, NativeObject, file)); + return av.ToBoolExprArray(); } /// /// Similar to ParseFile. Instead it takes as argument a string. - /// - - public BoolExpr[] ParseString(string s) { - return ToBoolExprs(new ASTVector(Context, Native.Z3_fixedpoint_from_string(Context.nCtx, NativeObject, s))); + /// + public BoolExpr[] ParseString(string s) + { + ASTVector av = new ASTVector(Context, Native.Z3_fixedpoint_from_string(Context.nCtx, NativeObject, s)); + return av.ToBoolExprArray(); } diff --git a/src/api/dotnet/Goal.cs b/src/api/dotnet/Goal.cs index 46d519d35..5ee44f34b 100644 --- a/src/api/dotnet/Goal.cs +++ b/src/api/dotnet/Goal.cs @@ -208,6 +208,21 @@ namespace Microsoft.Z3 return Native.Z3_goal_to_string(Context.nCtx, NativeObject); } + /// + /// Goal to BoolExpr conversion. + /// + /// A string representation of the Goal. + public BoolExpr AsBoolExpr() { + uint n = Size; + if (n == 0) + return Context.MkTrue(); + else if (n == 1) + return Formulas[0]; + else { + return Context.MkAnd(Formulas); + } + } + #region Internal internal Goal(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs index d54c8f634..595023b69 100644 --- a/src/api/dotnet/InterpolationContext.cs +++ b/src/api/dotnet/InterpolationContext.cs @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + using System; using System.Collections.Generic; using System.Linq; @@ -47,7 +53,7 @@ namespace Microsoft.Z3 /// For more information on interpolation please refer /// too the function Z3_get_interpolant in the C/C++ API, which is /// well documented. - public Expr[] GetInterpolant(Expr pf, Expr pat, Params p) + public BoolExpr[] GetInterpolant(Expr pf, Expr pat, Params p) { Contract.Requires(pf != null); Contract.Requires(pat != null); @@ -59,11 +65,7 @@ namespace Microsoft.Z3 CheckContextMatch(p); ASTVector seq = new ASTVector(this, Native.Z3_get_interpolant(nCtx, pf.NativeObject, pat.NativeObject, p.NativeObject)); - uint n = seq.Size; - Expr[] res = new Expr[n]; - for (uint i = 0; i < n; i++) - res[i] = Expr.Create(this, seq[i].NativeObject); - return res; + return seq.ToBoolExprArray(); } /// @@ -72,7 +74,7 @@ namespace Microsoft.Z3 /// For more information on interpolation please refer /// too the function Z3_compute_interpolant in the C/C++ API, which is /// well documented. - public Z3_lbool ComputeInterpolant(Expr pat, Params p, out ASTVector interp, out Model model) + public Z3_lbool ComputeInterpolant(Expr pat, Params p, out BoolExpr[] interp, out Model model) { Contract.Requires(pat != null); Contract.Requires(p != null); @@ -84,7 +86,7 @@ namespace Microsoft.Z3 IntPtr i = IntPtr.Zero, m = IntPtr.Zero; int r = Native.Z3_compute_interpolant(nCtx, pat.NativeObject, p.NativeObject, ref i, ref m); - interp = new ASTVector(this, i); + interp = new ASTVector(this, i).ToBoolExprArray(); model = new Model(this, m); return (Z3_lbool)r; } @@ -106,7 +108,7 @@ namespace Microsoft.Z3 /// For more information on interpolation please refer /// too the function Z3_check_interpolant in the C/C++ API, which is /// well documented. - public int CheckInterpolant(Expr[] cnsts, uint[] parents, Expr[] interps, out string error, Expr[] theory) + public int CheckInterpolant(Expr[] cnsts, uint[] parents, BoolExpr[] interps, out string error, Expr[] theory) { Contract.Requires(cnsts.Length == parents.Length); Contract.Requires(cnsts.Length == interps.Length + 1); diff --git a/src/api/dotnet/Microsoft.Z3.csproj b/src/api/dotnet/Microsoft.Z3.csproj index 2b5e08173..937edb69d 100644 --- a/src/api/dotnet/Microsoft.Z3.csproj +++ b/src/api/dotnet/Microsoft.Z3.csproj @@ -365,6 +365,7 @@ + diff --git a/src/api/dotnet/Model.cs b/src/api/dotnet/Model.cs index adf233a98..a7f62e6e8 100644 --- a/src/api/dotnet/Model.cs +++ b/src/api/dotnet/Model.cs @@ -265,12 +265,8 @@ namespace Microsoft.Z3 Contract.Requires(s != null); Contract.Ensures(Contract.Result() != null); - ASTVector nUniv = new ASTVector(Context, Native.Z3_model_get_sort_universe(Context.nCtx, NativeObject, s.NativeObject)); - uint n = nUniv.Size; - Expr[] res = new Expr[n]; - for (uint i = 0; i < n; i++) - res[i] = Expr.Create(Context, nUniv[i].NativeObject); - return res; + ASTVector av = new ASTVector(Context, Native.Z3_model_get_sort_universe(Context.nCtx, NativeObject, s.NativeObject)); + return av.ToExprArray(); } /// diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs new file mode 100644 index 000000000..501603668 --- /dev/null +++ b/src/api/dotnet/Optimize.cs @@ -0,0 +1,307 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + Optimize.cs + +Abstract: + + Z3 Managed API: Optimizes + +Author: + + Nikolaj Bjorner (nbjorner) 2013-12-03 + +Notes: + +--*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Z3 +{ + /// + /// Object for managing optimizization context + /// + [ContractVerification(true)] + public class Optimize : Z3Object + { + /// + /// A string that describes all available optimize solver parameters. + /// + public string Help + { + get + { + Contract.Ensures(Contract.Result() != null); + return Native.Z3_optimize_get_help(Context.nCtx, NativeObject); + } + } + + /// + /// Sets the optimize solver parameters. + /// + public Params Parameters + { + set + { + Contract.Requires(value != null); + Context.CheckContextMatch(value); + Native.Z3_optimize_set_params(Context.nCtx, NativeObject, value.NativeObject); + } + } + + /// + /// Retrieves parameter descriptions for Optimize solver. + /// + public ParamDescrs ParameterDescriptions + { + get { return new ParamDescrs(Context, Native.Z3_optimize_get_param_descrs(Context.nCtx, NativeObject)); } + } + + /// + /// Assert a constraint (or multiple) into the optimize solver. + /// + public void Assert(params BoolExpr[] constraints) + { + Contract.Requires(constraints != null); + Contract.Requires(Contract.ForAll(constraints, c => c != null)); + + Context.CheckContextMatch(constraints); + foreach (BoolExpr a in constraints) + { + Native.Z3_optimize_assert(Context.nCtx, NativeObject, a.NativeObject); + } + } + + /// + /// Alias for Assert. + /// + public void Add(params BoolExpr[] constraints) + { + Assert(constraints); + } + + /// + /// Handle to objectives returned by objective functions. + /// + public class Handle + { + Optimize opt; + uint handle; + internal Handle(Optimize opt, uint h) + { + this.opt = opt; + this.handle = h; + } + + /// + /// Retrieve a lower bound for the objective handle. + /// + public ArithExpr Lower + { + get { return opt.GetLower(handle); } + } + + /// + /// Retrieve an upper bound for the objective handle. + /// + public ArithExpr Upper + { + get { return opt.GetUpper(handle); } + } + + /// + /// Retrieve the value of an objective. + /// + public ArithExpr Value + { + get { return Lower; } + } + } + + /// + /// Assert soft constraint + /// + /// + /// Return an objective which associates with the group of constraints. + /// + public Handle AssertSoft(BoolExpr constraint, uint weight, string group) + { + Context.CheckContextMatch(constraint); + Symbol s = Context.MkSymbol(group); + return new Handle(this, Native.Z3_optimize_assert_soft(Context.nCtx, NativeObject, constraint.NativeObject, weight.ToString(), s.NativeObject)); + } + + + /// + /// Check satisfiability of asserted constraints. + /// Produce a model that (when the objectives are bounded and + /// don't use strict inequalities) meets the objectives. + /// + /// + public Status Check() + { + Z3_lbool r = (Z3_lbool)Native.Z3_optimize_check(Context.nCtx, NativeObject); + switch (r) + { + case Z3_lbool.Z3_L_TRUE: + return Status.SATISFIABLE; + case Z3_lbool.Z3_L_FALSE: + return Status.UNSATISFIABLE; + default: + return Status.UNKNOWN; + } + } + + /// + /// Creates a backtracking point. + /// + /// + public void Push() + { + Native.Z3_optimize_push(Context.nCtx, NativeObject); + } + + /// + /// Backtrack one backtracking point. + /// + /// Note that an exception is thrown if Pop is called without a corresponding Push + /// + public void Pop() + { + Native.Z3_optimize_pop(Context.nCtx, NativeObject); + } + + + /// + /// The model of the last Check. + /// + /// + /// The result is null if Check was not invoked before, + /// if its results was not SATISFIABLE, or if model production is not enabled. + /// + public Model Model + { + get + { + IntPtr x = Native.Z3_optimize_get_model(Context.nCtx, NativeObject); + if (x == IntPtr.Zero) + return null; + else + return new Model(Context, x); + } + } + + /// + /// Declare an arithmetical maximization objective. + /// Return a handle to the objective. The handle is used as + /// to retrieve the values of objectives after calling Check. + /// + public Handle MkMaximize(ArithExpr e) + { + return new Handle(this, Native.Z3_optimize_maximize(Context.nCtx, NativeObject, e.NativeObject)); + } + + /// + /// Declare an arithmetical minimization objective. + /// Similar to MkMaximize. + /// + public Handle MkMinimize(ArithExpr e) + { + return new Handle(this, Native.Z3_optimize_minimize(Context.nCtx, NativeObject, e.NativeObject)); + } + + /// + /// Retrieve a lower bound for the objective handle. + /// + private ArithExpr GetLower(uint index) + { + return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_lower(Context.nCtx, NativeObject, index)); + } + + + /// + /// Retrieve an upper bound for the objective handle. + /// + private ArithExpr GetUpper(uint index) + { + return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_upper(Context.nCtx, NativeObject, index)); + } + + /// + /// Return a string the describes why the last to check returned unknown + /// + public String getReasonUnknown() + { + Contract.Ensures(Contract.Result() != null); + return Native.Z3_optimize_get_reason_unknown(Context.nCtx, NativeObject); + } + + + /// + /// Print the context to a string (SMT-LIB parseable benchmark). + /// + public override string ToString() + { + return Native.Z3_optimize_to_string(Context.nCtx, NativeObject); + } + + /// + /// Optimize statistics. + /// + public Statistics Statistics + { + get + { + Contract.Ensures(Contract.Result() != null); + + return new Statistics(Context, Native.Z3_optimize_get_statistics(Context.nCtx, NativeObject)); + } + } + + + #region Internal + internal Optimize(Context ctx, IntPtr obj) + : base(ctx, obj) + { + Contract.Requires(ctx != null); + } + internal Optimize(Context ctx) + : base(ctx, Native.Z3_mk_optimize(ctx.nCtx)) + { + Contract.Requires(ctx != null); + } + + internal class DecRefQueue : IDecRefQueue + { + public DecRefQueue() : base() { } + public DecRefQueue(uint move_limit) : base(move_limit) { } + internal override void IncRef(Context ctx, IntPtr obj) + { + Native.Z3_optimize_inc_ref(ctx.nCtx, obj); + } + + internal override void DecRef(Context ctx, IntPtr obj) + { + Native.Z3_optimize_dec_ref(ctx.nCtx, obj); + } + }; + + internal override void IncRef(IntPtr o) + { + Context.Optimize_DRQ.IncAndClear(Context, o); + base.IncRef(o); + } + + internal override void DecRef(IntPtr o) + { + Context.Optimize_DRQ.Add(o); + base.DecRef(o); + } + #endregion + } +} diff --git a/src/api/dotnet/Params.cs b/src/api/dotnet/Params.cs index 81ca6727b..23b037c78 100644 --- a/src/api/dotnet/Params.cs +++ b/src/api/dotnet/Params.cs @@ -79,6 +79,7 @@ namespace Microsoft.Z3 Native.Z3_params_set_symbol(Context.nCtx, NativeObject, name.NativeObject, value.NativeObject); } + /// /// Adds a parameter setting. /// @@ -118,6 +119,7 @@ namespace Microsoft.Z3 /// public void Add(string name, string value) { + Contract.Requires(name != null); Contract.Requires(value != null); Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, Context.MkSymbol(value).NativeObject); diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index 012a283f5..80ca7a0cd 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -178,8 +178,8 @@ namespace Microsoft.Z3 { get { - ASTVector ass = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); - return ass.Size; + ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); + return assertions.Size; } } @@ -192,12 +192,8 @@ namespace Microsoft.Z3 { Contract.Ensures(Contract.Result() != null); - ASTVector ass = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); - uint n = ass.Size; - BoolExpr[] res = new BoolExpr[n]; - for (uint i = 0; i < n; i++) - res[i] = new BoolExpr(Context, ass[i].NativeObject); - return res; + ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_assertions(Context.nCtx, NativeObject)); + return assertions.ToBoolExprArray(); } } @@ -270,18 +266,14 @@ namespace Microsoft.Z3 /// The result is empty if Check was not invoked before, /// if its results was not UNSATISFIABLE, or if core production is disabled. /// - public Expr[] UnsatCore + public BoolExpr[] UnsatCore { get { Contract.Ensures(Contract.Result() != null); - ASTVector core = new ASTVector(Context, Native.Z3_solver_get_unsat_core(Context.nCtx, NativeObject)); - uint n = core.Size; - Expr[] res = new Expr[n]; - for (uint i = 0; i < n; i++) - res[i] = Expr.Create(Context, core[i].NativeObject); - return res; + ASTVector core = new ASTVector(Context, Native.Z3_solver_get_unsat_core(Context.nCtx, NativeObject)); + return core.ToBoolExprArray(); } } diff --git a/src/api/dotnet/Symbol.cs b/src/api/dotnet/Symbol.cs index 783647d3f..2a1fdf6c5 100644 --- a/src/api/dotnet/Symbol.cs +++ b/src/api/dotnet/Symbol.cs @@ -66,6 +66,47 @@ namespace Microsoft.Z3 throw new Z3Exception("Unknown symbol kind encountered"); } + + /// + /// Equality overloading. + /// + public static bool operator ==(Symbol s1, Symbol s2) + { + + return Object.ReferenceEquals(s1, s2) || + (!Object.ReferenceEquals(s1, null) && + !Object.ReferenceEquals(s2, null) && + s1.NativeObject == s2.NativeObject); + } + + /// + /// Equality overloading. + /// + public static bool operator !=(Symbol s1, Symbol s2) + { + return !(s1.NativeObject == s2.NativeObject); + } + + /// + /// Object comparison. + /// + public override bool Equals(object o) + { + Symbol casted = o as Symbol; + if (casted == null) return false; + return this == casted; + } + + /// + /// The Symbols's hash code. + /// + /// A hash code + public override int GetHashCode() + { + return (int)NativeObject; + } + + #region Internal /// /// Symbol constructor diff --git a/src/api/java/AST.java b/src/api/java/AST.java index 436a9fdcb..6b5493bf9 100644 --- a/src/api/java/AST.java +++ b/src/api/java/AST.java @@ -22,10 +22,8 @@ import com.microsoft.z3.enumerations.Z3_ast_kind; /** * The abstract syntax tree (AST) class. **/ -public class AST extends Z3Object +public class AST extends Z3Object implements Comparable { - /* Overloaded operators are not translated. */ - /** * Object comparison. * @@ -35,7 +33,7 @@ public class AST extends Z3Object { AST casted = null; - try + try { casted = AST.class.cast(o); } catch (ClassCastException e) @@ -43,8 +41,13 @@ public class AST extends Z3Object return false; } - return this.getNativeObject() == casted.getNativeObject(); - } + return + (this == casted) || + (this != null) && + (casted != null) && + (getContext().nCtx() == casted.getContext().nCtx()) && + (Native.isEqAst(getContext().nCtx(), getNativeObject(), casted.getNativeObject())); +} /** * Object Comparison. diff --git a/src/api/java/ASTMap.java b/src/api/java/ASTMap.java index 398aac77f..afeb868ca 100644 --- a/src/api/java/ASTMap.java +++ b/src/api/java/ASTMap.java @@ -92,10 +92,10 @@ class ASTMap extends Z3Object * * @throws Z3Exception **/ - public ASTVector getKeys() + public AST[] getKeys() { - return new ASTVector(getContext(), Native.astMapKeys(getContext().nCtx(), - getNativeObject())); + ASTVector av = new ASTVector(getContext(), Native.astMapKeys(getContext().nCtx(), getNativeObject())); + return av.ToArray(); } /** diff --git a/src/api/java/ASTVector.java b/src/api/java/ASTVector.java index 9c6504493..5b57db9d9 100644 --- a/src/api/java/ASTVector.java +++ b/src/api/java/ASTVector.java @@ -119,4 +119,135 @@ public class ASTVector extends Z3Object getContext().getASTVectorDRQ().add(o); super.decRef(o); } -} + + /** + * Translates the AST vector into an AST[] + * */ + public AST[] ToArray() + { + int n = size(); + AST[] res = new AST[n]; + for (int i = 0; i < n; i++) + res[i] = AST.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an Expr[] + * */ + public Expr[] ToExprArray() { + int n = size(); + Expr[] res = new Expr[n]; + for (int i = 0; i < n; i++) + res[i] = Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an BoolExpr[] + * */ + public BoolExpr[] ToBoolExprArray() + { + int n = size(); + BoolExpr[] res = new BoolExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (BoolExpr) Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an BitVecExpr[] + * */ + public BitVecExpr[] ToBitVecExprArray() + { + int n = size(); + BitVecExpr[] res = new BitVecExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (BitVecExpr)Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an ArithExpr[] + * */ + public ArithExpr[] ToArithExprExprArray() + { + int n = size(); + ArithExpr[] res = new ArithExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (ArithExpr)Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an ArrayExpr[] + * */ + public ArrayExpr[] ToArrayExprArray() + { + int n = size(); + ArrayExpr[] res = new ArrayExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (ArrayExpr)Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an DatatypeExpr[] + * */ + public DatatypeExpr[] ToDatatypeExprArray() + { + int n = size(); + DatatypeExpr[] res = new DatatypeExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (DatatypeExpr)Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an FPExpr[] + * */ + public FPExpr[] ToFPExprArray() + { + int n = size(); + FPExpr[] res = new FPExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (FPExpr)Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an FPRMExpr[] + * */ + public FPRMExpr[] ToFPRMExprArray() + { + int n = size(); + FPRMExpr[] res = new FPRMExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (FPRMExpr)Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an IntExpr[] + * */ + public IntExpr[] ToIntExprArray() + { + int n = size(); + IntExpr[] res = new IntExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (IntExpr)Expr.create(getContext(), get(i).getNativeObject()); + return res; + } + + /** + * Translates the AST vector into an RealExpr[] + * */ + public RealExpr[] ToRealExprArray() + { + int n = size(); + RealExpr[] res = new RealExpr[n]; + for (int i = 0; i < n; i++) + res[i] = (RealExpr)Expr.create(getContext(), get(i).getNativeObject()); + return res; + } +} \ No newline at end of file diff --git a/src/api/java/Context.java b/src/api/java/Context.java index fac4f5180..0955f79ca 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -361,6 +361,23 @@ public class Context extends IDisposable return mkDatatypeSorts(mkSymbols(names), c); } + /** + * Update a datatype field at expression t with value v. + * The function performs a record update at t. The field + * that is passed in as argument is updated with value v, + * the remainig fields of t are unchanged. + **/ + public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) + throws Z3Exception + { + return Expr.create + (this, + Native.datatypeUpdateField + (nCtx(), field.getNativeObject(), + t.getNativeObject(), v.getNativeObject())); + } + + /** * Creates a new function declaration. **/ @@ -1708,32 +1725,31 @@ public class Context extends IDisposable /** * Create an empty set. **/ - public Expr mkEmptySet(Sort domain) + public ArrayExpr mkEmptySet(Sort domain) { checkContextMatch(domain); - return Expr.create(this, + return (ArrayExpr)Expr.create(this, Native.mkEmptySet(nCtx(), domain.getNativeObject())); } /** * Create the full set. **/ - public Expr mkFullSet(Sort domain) + public ArrayExpr mkFullSet(Sort domain) { checkContextMatch(domain); - return Expr.create(this, + return (ArrayExpr)Expr.create(this, Native.mkFullSet(nCtx(), domain.getNativeObject())); } /** * Add an element to the set. **/ - public Expr mkSetAdd(Expr set, Expr element) + public ArrayExpr mkSetAdd(ArrayExpr set, Expr element) { checkContextMatch(set); checkContextMatch(element); - return Expr.create( - this, + return (ArrayExpr)Expr.create(this, Native.mkSetAdd(nCtx(), set.getNativeObject(), element.getNativeObject())); } @@ -1741,12 +1757,11 @@ public class Context extends IDisposable /** * Remove an element from a set. **/ - public Expr mkSetDel(Expr set, Expr element) + public ArrayExpr mkSetDel(ArrayExpr set, Expr element) { checkContextMatch(set); checkContextMatch(element); - return Expr.create( - this, + return (ArrayExpr)Expr.create(this, Native.mkSetDel(nCtx(), set.getNativeObject(), element.getNativeObject())); } @@ -1754,11 +1769,10 @@ public class Context extends IDisposable /** * Take the union of a list of sets. **/ - public Expr mkSetUnion(Expr... args) + public ArrayExpr mkSetUnion(ArrayExpr... args) { checkContextMatch(args); - return Expr.create( - this, + return (ArrayExpr)Expr.create(this, Native.mkSetUnion(nCtx(), (int) args.length, AST.arrayToNative(args))); } @@ -1766,11 +1780,10 @@ public class Context extends IDisposable /** * Take the intersection of a list of sets. **/ - public Expr mkSetIntersection(Expr... args) + public ArrayExpr mkSetIntersection(ArrayExpr... args) { checkContextMatch(args); - return Expr.create( - this, + return (ArrayExpr)Expr.create(this, Native.mkSetIntersect(nCtx(), (int) args.length, AST.arrayToNative(args))); } @@ -1778,12 +1791,11 @@ public class Context extends IDisposable /** * Take the difference between two sets. **/ - public Expr mkSetDifference(Expr arg1, Expr arg2) + public ArrayExpr mkSetDifference(ArrayExpr arg1, ArrayExpr arg2) { checkContextMatch(arg1); checkContextMatch(arg2); - return Expr.create( - this, + return (ArrayExpr)Expr.create(this, Native.mkSetDifference(nCtx(), arg1.getNativeObject(), arg2.getNativeObject())); } @@ -1791,22 +1803,21 @@ public class Context extends IDisposable /** * Take the complement of a set. **/ - public Expr mkSetComplement(Expr arg) + public ArrayExpr mkSetComplement(ArrayExpr arg) { checkContextMatch(arg); - return Expr.create(this, + return (ArrayExpr)Expr.create(this, Native.mkSetComplement(nCtx(), arg.getNativeObject())); } /** * Check for set membership. **/ - public Expr mkSetMembership(Expr elem, Expr set) + public BoolExpr mkSetMembership(Expr elem, ArrayExpr set) { checkContextMatch(elem); checkContextMatch(set); - return Expr.create( - this, + return (BoolExpr) Expr.create(this, Native.mkSetMember(nCtx(), elem.getNativeObject(), set.getNativeObject())); } @@ -1814,12 +1825,11 @@ public class Context extends IDisposable /** * Check for subsetness of sets. **/ - public Expr mkSetSubset(Expr arg1, Expr arg2) + public BoolExpr mkSetSubset(ArrayExpr arg1, ArrayExpr arg2) { checkContextMatch(arg1); checkContextMatch(arg2); - return Expr.create( - this, + return (BoolExpr) Expr.create(this, Native.mkSetSubset(nCtx(), arg1.getNativeObject(), arg2.getNativeObject())); } @@ -2786,6 +2796,14 @@ public class Context extends IDisposable return new Fixedpoint(this); } + /** + * Create a Optimize context. + **/ + public Optimize mkOptimize() + { + return new Optimize(this); + } + /** * Create the floating-point RoundingMode sort. @@ -3661,6 +3679,7 @@ public class Context extends IDisposable private StatisticsDecRefQueue m_Statistics_DRQ = new StatisticsDecRefQueue(10); private TacticDecRefQueue m_Tactic_DRQ = new TacticDecRefQueue(10); private FixedpointDecRefQueue m_Fixedpoint_DRQ = new FixedpointDecRefQueue(10); + private OptimizeDecRefQueue m_Optimize_DRQ = new OptimizeDecRefQueue(10); public IDecRefQueue getASTDRQ() { @@ -3737,6 +3756,11 @@ public class Context extends IDisposable return m_Fixedpoint_DRQ; } + public IDecRefQueue getOptimizeDRQ() + { + return m_Optimize_DRQ; + } + protected long m_refCount = 0; /** diff --git a/src/api/java/FPNum.java b/src/api/java/FPNum.java index 9a147778a..69a44c559 100644 --- a/src/api/java/FPNum.java +++ b/src/api/java/FPNum.java @@ -43,6 +43,21 @@ public class FPNum extends FPExpr return Native.fpaGetNumeralSignificandString(getContext().nCtx(), getNativeObject()); } + /** + * The significand value of a floating-point numeral as a UInt64 + * Remarks: This function extracts the significand bits, without the + * hidden bit or normalization. Throws an exception if the + * significand does not fit into a UInt64. + * @throws Z3Exception + **/ + public long getSignificandUInt64() + { + Native.LongPtr res = new Native.LongPtr(); + if (Native.fpaGetNumeralSignificandUint64(getContext().nCtx(), getNativeObject(), res) ^ true) + throw new Z3Exception("Significand is not a 64 bit unsigned integer"); + return res.value; + } + /** * Return the exponent value of a floating-point numeral as a string * @throws Z3Exception diff --git a/src/api/java/FPSort.java b/src/api/java/FPSort.java index 82e69afbe..59313fe27 100644 --- a/src/api/java/FPSort.java +++ b/src/api/java/FPSort.java @@ -43,7 +43,7 @@ public class FPSort extends Sort * The number of significand bits. */ public int getSBits() { - return Native.fpaGetEbits(getContext().nCtx(), getNativeObject()); + return Native.fpaGetSbits(getContext().nCtx(), getNativeObject()); } } diff --git a/src/api/java/Fixedpoint.java b/src/api/java/Fixedpoint.java index f87679cc1..0bdbfb571 100644 --- a/src/api/java/Fixedpoint.java +++ b/src/api/java/Fixedpoint.java @@ -213,7 +213,6 @@ public class Fixedpoint extends Z3Object **/ public String getReasonUnknown() { - return Native.fixedpointGetReasonUnknown(getContext().nCtx(), getNativeObject()); } @@ -295,14 +294,8 @@ public class Fixedpoint extends Z3Object **/ public BoolExpr[] getRules() { - - ASTVector v = new ASTVector(getContext(), Native.fixedpointGetRules( - getContext().nCtx(), getNativeObject())); - int n = v.size(); - BoolExpr[] res = new BoolExpr[n]; - for (int i = 0; i < n; i++) - res[i] = new BoolExpr(getContext(), v.get(i).getNativeObject()); - return res; + ASTVector v = new ASTVector(getContext(), Native.fixedpointGetRules(getContext().nCtx(), getNativeObject())); + return v.ToBoolExprArray(); } /** @@ -312,17 +305,45 @@ public class Fixedpoint extends Z3Object **/ public BoolExpr[] getAssertions() { - - ASTVector v = new ASTVector(getContext(), Native.fixedpointGetAssertions( - getContext().nCtx(), getNativeObject())); - int n = v.size(); - BoolExpr[] res = new BoolExpr[n]; - for (int i = 0; i < n; i++) - res[i] = new BoolExpr(getContext(), v.get(i).getNativeObject()); - return res; + ASTVector v = new ASTVector(getContext(), Native.fixedpointGetAssertions(getContext().nCtx(), getNativeObject())); + return v.ToBoolExprArray(); } - Fixedpoint(Context ctx, long obj) + /** + * Fixedpoint statistics. + * + * @throws Z3Exception + **/ + public Statistics getStatistics() + { + return new Statistics(getContext(), Native.fixedpointGetStatistics( + getContext().nCtx(), getNativeObject())); + } + + /** + * Parse an SMT-LIB2 file with fixedpoint rules. + * Add the rules to the current fixedpoint context. + * Return the set of queries in the file. + **/ + public BoolExpr[] ParseFile(String file) + { + ASTVector av = new ASTVector(getContext(), Native.fixedpointFromFile(getContext().nCtx(), getNativeObject(), file)); + return av.ToBoolExprArray(); + } + + /** + * Parse an SMT-LIB2 string with fixedpoint rules. + * Add the rules to the current fixedpoint context. + * Return the set of queries in the file. + **/ + public BoolExpr[] ParseString(String s) + { + ASTVector av = new ASTVector(getContext(), Native.fixedpointFromString(getContext().nCtx(), getNativeObject(), s)); + return av.ToBoolExprArray(); + } + + + Fixedpoint(Context ctx, long obj) throws Z3Exception { super(ctx, obj); } diff --git a/src/api/java/FuncDecl.java b/src/api/java/FuncDecl.java index f7e9de2b2..bc2baab91 100644 --- a/src/api/java/FuncDecl.java +++ b/src/api/java/FuncDecl.java @@ -26,31 +26,25 @@ import com.microsoft.z3.enumerations.Z3_parameter_kind; **/ public class FuncDecl extends AST { - /** - * Comparison operator. - * - * @return True if {@code a"/> and and context + val nc_of_ast : ast -> Z3native.z3_context + val ptr_of_ast : ast -> Z3native.ptr + val ast_of_ptr : context -> Z3native.ptr -> ast + module ASTVector : + sig + type ast_vector = z3_native_object + val create : context -> Z3native.ptr -> ast_vector + val mk_ast_vector : context -> ast_vector + val get_size : ast_vector -> int + val get : ast_vector -> int -> ast + val set : ast_vector -> int -> ast -> unit + val resize : ast_vector -> int -> unit + val push : ast_vector -> ast -> unit + val translate : ast_vector -> context -> ast_vector + val to_list : ast_vector -> ast list + val to_expr_list : ast_vector -> Expr.expr list + val to_string : ast_vector -> string + end + module ASTMap : + sig + type ast_map = z3_native_object + val create : context -> Z3native.ptr -> ast_map + val mk_ast_map : context -> ast_map + val contains : ast_map -> ast -> bool + val find : ast_map -> ast -> ast + val insert : ast_map -> ast -> ast -> unit + val erase : ast_map -> ast -> unit + val reset : ast_map -> unit + val get_size : ast_map -> int + val get_keys : ast_map -> ast list + val to_string : ast_map -> string + end + val hash : ast -> int + val get_id : ast -> int + val get_ast_kind : ast -> Z3enums.ast_kind + val is_expr : ast -> bool + val is_app : ast -> bool + val is_var : ast -> bool + val is_quantifier : ast -> bool + val is_sort : ast -> bool + val is_func_decl : ast -> bool + val to_string : ast -> string + val to_sexpr : ast -> string + val equal : ast -> ast -> bool + val compare : ast -> ast -> int + val translate : ast -> context -> ast + val unwrap_ast : ast -> Z3native.ptr + val wrap_ast : context -> Z3native.z3_ast -> ast +end = struct type ast = z3_native_object let context_of_ast ( x : ast ) = (z3obj_gc x) @@ -216,13 +267,13 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = let res : ast_vector = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.ast_vector_inc_ref ; - dec_ref = Z3native.ast_vector_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.ast_vector_inc_ref ; + dec_ref = Z3native.ast_vector_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res - + let mk_ast_vector ( ctx : context ) = (create ctx (Z3native.mk_ast_vector (context_gno ctx))) let get_size ( x : ast_vector ) = @@ -242,6 +293,16 @@ struct let translate ( x : ast_vector ) ( to_ctx : context ) = create to_ctx (Z3native.ast_vector_translate (z3obj_gnc x) (z3obj_gno x) (context_gno to_ctx)) + + let to_list ( x : ast_vector ) = + let xs = (get_size x) in + let f i = (get x i) in + mk_list f xs + + let to_expr_list ( x : ast_vector ) = + let xs = (get_size x) in + let f i = (Expr.expr_of_ptr (z3obj_gc x) (z3obj_gno (get x i))) in + mk_list f xs let to_string ( x : ast_vector ) = Z3native.ast_vector_to_string (z3obj_gnc x) (z3obj_gno x) @@ -253,9 +314,9 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = let res : ast_map = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.ast_map_inc_ref ; - dec_ref = Z3native.ast_map_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.ast_map_inc_ref ; + dec_ref = Z3native.ast_map_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res @@ -291,8 +352,7 @@ struct let get_keys ( x : ast_map ) = let av = ASTVector.create (z3obj_gc x) (Z3native.ast_map_keys (z3obj_gnc x) (z3obj_gno x)) in - let f i = (ASTVector.get av i) in - mk_list f (ASTVector.get_size av) + (ASTVector.to_list av) let to_string ( x : ast_map ) = Z3native.ast_map_to_string (z3obj_gnc x) (z3obj_gno x) @@ -341,31 +401,43 @@ struct let wrap_ast ( ctx : context ) ( ptr : Z3native.ptr ) = ast_of_ptr ctx ptr end -open AST - - -module Sort = -struct +and Sort : +sig + type sort = Sort of AST.ast + val ast_of_sort : Sort.sort -> AST.ast + val sort_of_ptr : context -> Z3native.ptr -> sort + val gc : sort -> context + val gnc : sort -> Z3native.ptr + val gno : sort -> Z3native.ptr + val sort_lton : sort list -> Z3native.ptr array + val sort_option_lton : sort option list -> Z3native.ptr array + val equal : sort -> sort -> bool + val get_id : sort -> int + val get_sort_kind : sort -> Z3enums.sort_kind + val get_name : sort -> Symbol.symbol + val to_string : sort -> string + val mk_uninterpreted : context -> Symbol.symbol -> sort + val mk_uninterpreted_s : context -> string -> sort +end = struct type sort = Sort of AST.ast let sort_of_ptr : context -> Z3native.ptr -> sort = fun ctx no -> - let q = (z3_native_object_of_ast_ptr ctx no) in if ((Z3enums.ast_kind_of_int (Z3native.get_ast_kind (context_gno ctx) no)) != Z3enums.SORT_AST) then raise (Z3native.Exception "Invalid coercion") else match (sort_kind_of_int (Z3native.get_sort_kind (context_gno ctx) no)) with - | ARRAY_SORT - | BOOL_SORT - | BV_SORT - | DATATYPE_SORT - | INT_SORT - | REAL_SORT - | UNINTERPRETED_SORT - | FINITE_DOMAIN_SORT - | RELATION_SORT - | FLOATING_POINT_SORT - | ROUNDING_MODE_SORT -> Sort(q) - | UNKNOWN_SORT -> raise (Z3native.Exception "Unknown sort kind encountered") + | ARRAY_SORT + | BOOL_SORT + | BV_SORT + | DATATYPE_SORT + | INT_SORT + | REAL_SORT + | UNINTERPRETED_SORT + | FINITE_DOMAIN_SORT + | RELATION_SORT + | FLOATING_POINT_SORT + | ROUNDING_MODE_SORT -> Sort(z3_native_object_of_ast_ptr ctx no) + | UNKNOWN_SORT -> raise (Z3native.Exception "Unknown sort kind encountered") let ast_of_sort s = match s with Sort(x) -> x @@ -387,7 +459,7 @@ struct false else (Z3native.is_eq_sort (gnc a) (gno a) (gno b)) - + let get_id ( x : sort ) = Z3native.get_sort_id (gnc x) (gno x) let get_sort_kind ( x : sort ) = (sort_kind_of_int (Z3native.get_sort_kind (gnc x) (gno x))) @@ -407,10 +479,7 @@ struct mk_uninterpreted ctx (Symbol.mk_string ( ctx : context ) s) end -open Sort - - -module rec FuncDecl : +and FuncDecl : sig type func_decl = FuncDecl of AST.ast val ast_of_func_decl : FuncDecl.func_decl -> AST.ast @@ -467,21 +536,21 @@ end = struct let ast_of_func_decl f = match f with FuncDecl(x) -> x - let create_ndr ( ctx : context ) ( name : Symbol.symbol ) ( domain : sort list ) ( range : sort ) = + let create_ndr ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort list ) ( range : Sort.sort ) = let res = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.inc_ref ; dec_ref = Z3native.dec_ref } in - (z3obj_sno res ctx (Z3native.mk_func_decl (context_gno ctx) (Symbol.gno name) (List.length domain) (sort_lton domain) (Sort.gno range))) ; + (z3obj_sno res ctx (Z3native.mk_func_decl (context_gno ctx) (Symbol.gno name) (List.length domain) (Sort.sort_lton domain) (Sort.gno range))) ; (z3obj_create res) ; FuncDecl(res) - let create_pdr ( ctx : context) ( prefix : string ) ( domain : sort list ) ( range : sort ) = + let create_pdr ( ctx : context) ( prefix : string ) ( domain : Sort.sort list ) ( range : Sort.sort ) = let res = { m_ctx = ctx ; m_n_obj = null ; inc_ref = Z3native.inc_ref ; dec_ref = Z3native.dec_ref } in - (z3obj_sno res ctx (Z3native.mk_fresh_func_decl (context_gno ctx) prefix (List.length domain) (sort_lton domain) (Sort.gno range))) ; + (z3obj_sno res ctx (Z3native.mk_fresh_func_decl (context_gno ctx) prefix (List.length domain) (Sort.sort_lton domain) (Sort.gno range))) ; (z3obj_create res) ; FuncDecl(res) @@ -546,22 +615,22 @@ end = struct | _ -> raise (Z3native.Exception "parameter is not a rational string") end - let mk_func_decl ( ctx : context ) ( name : Symbol.symbol ) ( domain : sort list ) ( range : sort ) = + let mk_func_decl ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort list ) ( range : Sort.sort ) = create_ndr ctx name domain range - let mk_func_decl_s ( ctx : context ) ( name : string ) ( domain : sort list ) ( range : sort ) = + let mk_func_decl_s ( ctx : context ) ( name : string ) ( domain : Sort.sort list ) ( range : Sort.sort ) = mk_func_decl ctx (Symbol.mk_string ctx name) domain range - let mk_fresh_func_decl ( ctx : context ) ( prefix : string ) ( domain : sort list ) ( range : sort ) = + let mk_fresh_func_decl ( ctx : context ) ( prefix : string ) ( domain : Sort.sort list ) ( range : Sort.sort ) = create_pdr ctx prefix domain range - let mk_const_decl ( ctx : context ) ( name : Symbol.symbol ) ( range : sort ) = + let mk_const_decl ( ctx : context ) ( name : Symbol.symbol ) ( range : Sort.sort ) = create_ndr ctx name [] range - let mk_const_decl_s ( ctx : context ) ( name : string ) ( range : sort ) = + let mk_const_decl_s ( ctx : context ) ( name : string ) ( range : Sort.sort ) = create_ndr ctx (Symbol.mk_string ctx name) [] range - let mk_fresh_const_decl ( ctx : context ) ( prefix : string ) ( range : sort ) = + let mk_fresh_const_decl ( ctx : context ) ( prefix : string ) ( range : Sort.sort ) = create_pdr ctx prefix [] range @@ -581,11 +650,11 @@ end = struct let get_domain ( x : func_decl ) = let n = (get_domain_size x) in - let f i = sort_of_ptr (gc x) (Z3native.get_domain (gnc x) (gno x) i) in + let f i = Sort.sort_of_ptr (gc x) (Z3native.get_domain (gnc x) (gno x) i) in mk_list f n let get_range ( x : func_decl ) = - sort_of_ptr (gc x) (Z3native.get_range (gnc x) (gno x)) + Sort.sort_of_ptr (gc x) (Z3native.get_range (gnc x) (gno x)) let get_decl_kind ( x : func_decl ) = (decl_kind_of_int (Z3native.get_decl_kind (gnc x) (gno x))) @@ -599,7 +668,7 @@ end = struct | PARAMETER_INT -> Parameter.P_Int (Z3native.get_decl_int_parameter (gnc x) (gno x) i) | PARAMETER_DOUBLE -> Parameter.P_Dbl (Z3native.get_decl_double_parameter (gnc x) (gno x) i) | PARAMETER_SYMBOL-> Parameter.P_Sym (Symbol.create (gc x) (Z3native.get_decl_symbol_parameter (gnc x) (gno x) i)) - | PARAMETER_SORT -> Parameter.P_Srt (sort_of_ptr (gc x) (Z3native.get_decl_sort_parameter (gnc x) (gno x) i)) + | PARAMETER_SORT -> Parameter.P_Srt (Sort.sort_of_ptr (gc x) (Z3native.get_decl_sort_parameter (gnc x) (gno x) i)) | PARAMETER_AST -> Parameter.P_Ast (AST.ast_of_ptr (gc x) (Z3native.get_decl_ast_parameter (gnc x) (gno x) i)) | PARAMETER_FUNC_DECL -> Parameter.P_Fdl (func_decl_of_ptr (gc x) (Z3native.get_decl_func_decl_parameter (gnc x) (gno x) i)) | PARAMETER_RATIONAL -> Parameter.P_Rat (Z3native.get_decl_rational_parameter (gnc x) (gno x) i) @@ -820,29 +889,29 @@ end = struct let is_well_sorted ( x : expr ) = Z3native.is_well_sorted (gnc x) (gno x) - let get_sort ( x : expr ) = sort_of_ptr (Expr.gc x) (Z3native.get_sort (gnc x) (gno x)) + let get_sort ( x : expr ) = Sort.sort_of_ptr (Expr.gc x) (Z3native.get_sort (gnc x) (gno x)) let is_const ( x : expr ) = (match x with Expr(a) -> (AST.is_app a)) && (get_num_args x) == 0 && (FuncDecl.get_domain_size (get_func_decl x)) == 0 - let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( range : sort ) = + let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( range : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_const (context_gno ctx) (Symbol.gno name) (Sort.gno range)) - let mk_const_s ( ctx : context ) ( name : string ) ( range : sort ) = + let mk_const_s ( ctx : context ) ( name : string ) ( range : Sort.sort ) = mk_const ctx (Symbol.mk_string ctx name) range let mk_const_f ( ctx : context ) ( f : FuncDecl.func_decl ) = Expr.expr_of_func_app ctx f [] - let mk_fresh_const ( ctx : context ) ( prefix : string ) ( range : sort ) = + let mk_fresh_const ( ctx : context ) ( prefix : string ) ( range : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fresh_const (context_gno ctx) prefix (Sort.gno range)) let mk_app ( ctx : context ) ( f : FuncDecl.func_decl ) ( args : expr list ) = expr_of_func_app ctx f args - let mk_numeral_string ( ctx : context ) ( v : string ) ( ty : sort ) = + let mk_numeral_string ( ctx : context ) ( v : string ) ( ty : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno ty)) - let mk_numeral_int ( ctx : context ) ( v : int ) ( ty : sort ) = + let mk_numeral_int ( ctx : context ) ( v : int ) ( ty : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_int (context_gno ctx) v (Sort.gno ty)) let equal ( a : expr ) ( b : expr ) = AST.equal (ast_of_expr a) (ast_of_expr b) @@ -856,7 +925,7 @@ open Expr module Boolean = struct let mk_sort ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_bool_sort (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_bool_sort (context_gno ctx))) let mk_const ( ctx : context ) ( name : Symbol.symbol ) = (Expr.mk_const ctx name (mk_sort ctx)) @@ -943,7 +1012,7 @@ struct module Pattern = struct - type pattern = Pattern of ast + type pattern = Pattern of AST.ast let ast_of_pattern e = match e with Pattern(x) -> x @@ -1002,13 +1071,13 @@ struct let get_bound_variable_sorts ( x : quantifier ) = let n = (get_num_bound x) in - let f i = (sort_of_ptr (gc x) (Z3native.get_quantifier_bound_sort (gnc x) (gno x) i)) in + let f i = (Sort.sort_of_ptr (gc x) (Z3native.get_quantifier_bound_sort (gnc x) (gno x) i)) in mk_list f n let get_body ( x : quantifier ) = expr_of_ptr (gc x) (Z3native.get_quantifier_body (gnc x) (gno x)) - let mk_bound ( ctx : context ) ( index : int ) ( ty : sort ) = + let mk_bound ( ctx : context ) ( index : int ) ( ty : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_bound (context_gno ctx) index (Sort.gno ty)) let mk_pattern ( ctx : context ) ( terms : expr list ) = @@ -1017,14 +1086,14 @@ struct else Pattern.Pattern(z3_native_object_of_ast_ptr ctx (Z3native.mk_pattern (context_gno ctx) (List.length terms) (expr_lton terms))) - let mk_forall ( ctx : context ) ( sorts : sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = + let mk_forall ( ctx : context ) ( sorts : Sort.sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (List.length sorts) != (List.length names) then raise (Z3native.Exception "Number of sorts does not match number of names") else if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier (context_gno ctx) true (match weight with | None -> 1 | Some(x) -> x) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (List.length sorts) (sort_lton sorts) + (List.length sorts) (Sort.sort_lton sorts) (Symbol.symbol_lton names) (Expr.gno body))) else @@ -1034,7 +1103,7 @@ struct (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (List.length nopatterns) (expr_lton nopatterns) - (List.length sorts) (sort_lton sorts) + (List.length sorts) (Sort.sort_lton sorts) (Symbol.symbol_lton names) (Expr.gno body))) @@ -1055,14 +1124,14 @@ struct (List.length nopatterns) (expr_lton nopatterns) (Expr.gno body))) - let mk_exists ( ctx : context ) ( sorts : sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = + let mk_exists ( ctx : context ) ( sorts : Sort.sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (List.length sorts) != (List.length names) then raise (Z3native.Exception "Number of sorts does not match number of names") else if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier (context_gno ctx) false (match weight with | None -> 1 | Some(x) -> x) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (List.length sorts) (sort_lton sorts) + (List.length sorts) (Sort.sort_lton sorts) (Symbol.symbol_lton names) (Expr.gno body))) else @@ -1072,7 +1141,7 @@ struct (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) (List.length nopatterns) (expr_lton nopatterns) - (List.length sorts) (sort_lton sorts) + (List.length sorts) (Sort.sort_lton sorts) (Symbol.symbol_lton names) (Expr.gno body))) @@ -1093,7 +1162,7 @@ struct (List.length nopatterns) (expr_lton nopatterns) (Expr.gno body))) - let mk_quantifier ( ctx : context ) ( universal : bool ) ( sorts : sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = + let mk_quantifier ( ctx : context ) ( universal : bool ) ( sorts : Sort.sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (universal) then (mk_forall ctx sorts names body weight patterns nopatterns quantifier_id skolem_id) else @@ -1111,8 +1180,8 @@ end module Z3Array = struct - let mk_sort ( ctx : context ) ( domain : sort ) ( range : sort ) = - sort_of_ptr ctx (Z3native.mk_array_sort (context_gno ctx) (Sort.gno domain) (Sort.gno range)) + let mk_sort ( ctx : context ) ( domain : Sort.sort ) ( range : Sort.sort ) = + Sort.sort_of_ptr ctx (Z3native.mk_array_sort (context_gno ctx) (Sort.gno domain) (Sort.gno range)) let is_store ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_STORE) let is_select ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SELECT) @@ -1124,13 +1193,13 @@ struct (Z3native.is_app (Expr.gnc x) (Expr.gno x)) && ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gnc x) (Z3native.get_sort (Expr.gnc x) (Expr.gno x)))) == ARRAY_SORT) - let get_domain ( x : sort ) = Sort.sort_of_ptr (Sort.gc x) (Z3native.get_array_sort_domain (Sort.gnc x) (Sort.gno x)) - let get_range ( x : sort ) = Sort.sort_of_ptr (Sort.gc x) (Z3native.get_array_sort_range (Sort.gnc x) (Sort.gno x)) + let get_domain ( x : Sort.sort ) = Sort.sort_of_ptr (Sort.gc x) (Z3native.get_array_sort_domain (Sort.gnc x) (Sort.gno x)) + let get_range ( x : Sort.sort ) = Sort.sort_of_ptr (Sort.gc x) (Z3native.get_array_sort_range (Sort.gnc x) (Sort.gno x)) - let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( domain : sort ) ( range : sort ) = + let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort ) ( range : Sort.sort ) = (Expr.mk_const ctx name (mk_sort ctx domain range)) - let mk_const_s ( ctx : context ) ( name : string ) ( domain : sort ) ( range : sort ) = + let mk_const_s ( ctx : context ) ( name : string ) ( domain : Sort.sort ) ( range : Sort.sort ) = mk_const ctx (Symbol.mk_string ctx name) domain range let mk_select ( ctx : context ) ( a : expr ) ( i : expr ) = @@ -1139,7 +1208,7 @@ struct let mk_store ( ctx : context ) ( a : expr ) ( i : expr ) ( v : expr ) = expr_of_ptr ctx (Z3native.mk_store (context_gno ctx) (Expr.gno a) (Expr.gno i) (Expr.gno v)) - let mk_const_array ( ctx : context ) ( domain : sort ) ( v : expr ) = + let mk_const_array ( ctx : context ) ( domain : Sort.sort ) ( v : expr ) = expr_of_ptr ctx (Z3native.mk_const_array (context_gno ctx) (Sort.gno domain) (Expr.gno v)) let mk_map ( ctx : context ) ( f : func_decl ) ( args : expr list ) = @@ -1153,8 +1222,8 @@ end module Set = struct - let mk_sort ( ctx : context ) ( ty : sort ) = - sort_of_ptr ctx (Z3native.mk_set_sort (context_gno ctx) (Sort.gno ty)) + let mk_sort ( ctx : context ) ( ty : Sort.sort ) = + Sort.sort_of_ptr ctx (Z3native.mk_set_sort (context_gno ctx) (Sort.gno ty)) let is_union ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SET_UNION) let is_intersect ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SET_INTERSECT) @@ -1163,10 +1232,10 @@ struct let is_subset ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_SET_SUBSET) - let mk_empty ( ctx : context ) ( domain : sort ) = + let mk_empty ( ctx : context ) ( domain : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_empty_set (context_gno ctx) (Sort.gno domain))) - let mk_full ( ctx : context ) ( domain : sort ) = + let mk_full ( ctx : context ) ( domain : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_full_set (context_gno ctx) (Sort.gno domain)) let mk_set_add ( ctx : context ) ( set : expr ) ( element : expr ) = @@ -1199,7 +1268,7 @@ end module FiniteDomain = struct let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( size : int ) = - (sort_of_ptr ctx (Z3native.mk_finite_domain_sort (context_gno ctx) (Symbol.gno name) size)) + Sort.sort_of_ptr ctx (Z3native.mk_finite_domain_sort (context_gno ctx) (Symbol.gno name) size) let mk_sort_s ( ctx : context ) ( name : string ) ( size : int ) = mk_sort ctx (Symbol.mk_string ctx name) size @@ -1211,7 +1280,7 @@ struct let is_lt ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FD_LT) - let get_size ( x : sort ) = + let get_size ( x : Sort.sort ) = let (r, v) = (Z3native.get_finite_domain_sort_size (Sort.gnc x) (Sort.gno x)) in if r then v else raise (Z3native.Exception "Conversion failed.") @@ -1239,11 +1308,11 @@ struct let is_select ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_SELECT) let is_clone ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_CLONE) - let get_arity ( x : sort ) = Z3native.get_relation_arity (Sort.gnc x) (Sort.gno x) + let get_arity ( x : Sort.sort ) = Z3native.get_relation_arity (Sort.gnc x) (Sort.gno x) - let get_column_sorts ( x : sort ) = + let get_column_sorts ( x : Sort.sort ) = let n = get_arity x in - let f i = (sort_of_ptr (Sort.gc x) (Z3native.get_relation_column (Sort.gnc x) (Sort.gno x) i)) in + let f i = (Sort.sort_of_ptr (Sort.gc x) (Z3native.get_relation_column (Sort.gnc x) (Sort.gno x) i)) in mk_list f n end @@ -1262,7 +1331,7 @@ struct let _field_nums = FieldNumTable.create 0 - let create ( ctx : context ) ( name : Symbol.symbol ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : sort option list ) ( sort_refs : int list ) = + let create ( ctx : context ) ( name : Symbol.symbol ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : Sort.sort option list ) ( sort_refs : int list ) = let n = (List.length field_names) in if n != (List.length sorts) then raise (Z3native.Exception "Number of field names does not match number of sorts") @@ -1274,7 +1343,7 @@ struct (Symbol.gno recognizer) n (Symbol.symbol_lton field_names) - (sort_option_lton sorts) + (Sort.sort_option_lton sorts) (Array.of_list sort_refs)) in let no : constructor = { m_ctx = ctx ; m_n_obj = null ; @@ -1321,17 +1390,17 @@ struct res end - let mk_constructor ( ctx : context ) ( name : Symbol.symbol ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : sort option list ) ( sort_refs : int list ) = + let mk_constructor ( ctx : context ) ( name : Symbol.symbol ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : Sort.sort option list ) ( sort_refs : int list ) = Constructor.create ctx name recognizer field_names sorts sort_refs - let mk_constructor_s ( ctx : context ) ( name : string ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : sort option list ) ( sort_refs : int list ) = + let mk_constructor_s ( ctx : context ) ( name : string ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : Sort.sort option list ) ( sort_refs : int list ) = mk_constructor ctx (Symbol.mk_string ctx name) recognizer field_names sorts sort_refs let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( constructors : Constructor.constructor list ) = let f x = (z3obj_gno x) in let (x,_) = (Z3native.mk_datatype (context_gno ctx) (Symbol.gno name) (List.length constructors) (Array.of_list (List.map f constructors))) in - sort_of_ptr ctx x + Sort.sort_of_ptr ctx x let mk_sort_s ( ctx : context ) ( name : string ) ( constructors : Constructor.constructor list ) = mk_sort ctx (Symbol.mk_string ctx name) constructors @@ -1341,7 +1410,7 @@ struct let f e = (AST.ptr_of_ast (ConstructorList.create ctx e)) in let cla = (Array.of_list (List.map f c)) in let (r, a) = (Z3native.mk_datatypes (context_gno ctx) n (Symbol.symbol_lton names) cla) in - let g i = (sort_of_ptr ctx (Array.get r i)) in + let g i = (Sort.sort_of_ptr ctx (Array.get r i)) in mk_list g (Array.length r) let mk_sorts_s ( ctx : context ) ( names : string list ) ( c : Constructor.constructor list list ) = @@ -1352,19 +1421,19 @@ struct ) c - let get_num_constructors ( x : sort ) = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) + let get_num_constructors ( x : Sort.sort ) = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) - let get_constructors ( x : sort ) = + let get_constructors ( x : Sort.sort ) = let n = (get_num_constructors x) in let f i = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) i) in mk_list f n - let get_recognizers ( x : sort ) = + let get_recognizers ( x : Sort.sort ) = let n = (get_num_constructors x) in let f i = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) i) in mk_list f n - let get_accessors ( x : sort ) = + let get_accessors ( x : Sort.sort ) = let n = (get_num_constructors x) in let f i = ( let fd = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) i) in @@ -1380,80 +1449,80 @@ module Enumeration = struct let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( enum_names : Symbol.symbol list ) = let (a, _, _) = (Z3native.mk_enumeration_sort (context_gno ctx) (Symbol.gno name) (List.length enum_names) (Symbol.symbol_lton enum_names)) in - sort_of_ptr ctx a + Sort.sort_of_ptr ctx a let mk_sort_s ( ctx : context ) ( name : string ) ( enum_names : string list ) = mk_sort ctx (Symbol.mk_string ctx name) (Symbol.mk_strings ctx enum_names) - let get_const_decls ( x : sort ) = + let get_const_decls ( x : Sort.sort ) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) in let f i = (func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) i)) in mk_list f n - let get_const_decl ( x : sort ) ( inx : int ) = + let get_const_decl ( x : Sort.sort ) ( inx : int ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) inx) - let get_consts ( x : sort ) = + let get_consts ( x : Sort.sort ) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) in let f i = (Expr.mk_const_f (Sort.gc x) (get_const_decl x i)) in mk_list f n - let get_const ( x : sort ) ( inx : int ) = + let get_const ( x : Sort.sort ) ( inx : int ) = Expr.mk_const_f (Sort.gc x) (get_const_decl x inx) - let get_tester_decls ( x : sort ) = + let get_tester_decls ( x : Sort.sort ) = let n = Z3native.get_datatype_sort_num_constructors (Sort.gnc x) (Sort.gno x) in let f i = (func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) i)) in mk_list f n - let get_tester_decl ( x : sort ) ( inx : int ) = + let get_tester_decl ( x : Sort.sort ) ( inx : int ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) inx) end module Z3List = struct - let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( elem_sort : sort ) = + let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( elem_sort : Sort.sort ) = let (r, _, _, _, _, _, _) = (Z3native.mk_list_sort (context_gno ctx) (Symbol.gno name) (Sort.gno elem_sort)) in - sort_of_ptr ctx r + Sort.sort_of_ptr ctx r - let mk_list_s ( ctx : context ) (name : string) elem_sort = + let mk_list_s ( ctx : context ) ( name : string ) elem_sort = mk_sort ctx (Symbol.mk_string ctx name) elem_sort - let get_nil_decl ( x : sort ) = + let get_nil_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) 0) - let get_is_nil_decl ( x : sort ) = + let get_is_nil_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) 0) - let get_cons_decl ( x : sort ) = + let get_cons_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor (Sort.gnc x) (Sort.gno x) 1) - let get_is_cons_decl ( x : sort ) = + let get_is_cons_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_recognizer (Sort.gnc x) (Sort.gno x) 1) - let get_head_decl ( x : sort ) = + let get_head_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor_accessor (Sort.gnc x) (Sort.gno x) 1 0) - let get_tail_decl ( x : sort ) = + let get_tail_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_datatype_sort_constructor_accessor (Sort.gnc x) (Sort.gno x) 1 1) - let nil ( x : sort ) = expr_of_func_app (Sort.gc x) (get_nil_decl x) [] + let nil ( x : Sort.sort ) = expr_of_func_app (Sort.gc x) (get_nil_decl x) [] end module Tuple = struct - let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( field_sorts : sort list ) = - let (r, _, _) = (Z3native.mk_tuple_sort (context_gno ctx) (Symbol.gno name) (List.length field_names) (Symbol.symbol_lton field_names) (sort_lton field_sorts)) in - sort_of_ptr ctx r + let mk_sort ( ctx : context ) ( name : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( field_sorts : Sort.sort list ) = + let (r, _, _) = (Z3native.mk_tuple_sort (context_gno ctx) (Symbol.gno name) (List.length field_names) (Symbol.symbol_lton field_names) (Sort.sort_lton field_sorts)) in + Sort.sort_of_ptr ctx r - let get_mk_decl ( x : sort ) = + let get_mk_decl ( x : Sort.sort ) = func_decl_of_ptr (Sort.gc x) (Z3native.get_tuple_sort_mk_decl (Sort.gnc x) (Sort.gno x)) - let get_num_fields ( x : sort ) = Z3native.get_tuple_sort_num_fields (Sort.gnc x) (Sort.gno x) + let get_num_fields ( x : Sort.sort ) = Z3native.get_tuple_sort_num_fields (Sort.gnc x) (Sort.gno x) - let get_field_decls ( x : sort ) = + let get_field_decls ( x : Sort.sort ) = let n = get_num_fields x in let f i = func_decl_of_ptr (Sort.gc x) (Z3native.get_tuple_sort_field_decl (Sort.gnc x) (Sort.gno x) i) in mk_list f n @@ -1510,7 +1579,7 @@ struct module Integer = struct let mk_sort ( ctx : context ) = - sort_of_ptr ctx (Z3native.mk_int_sort (context_gno ctx)) + Sort.sort_of_ptr ctx (Z3native.mk_int_sort (context_gno ctx)) let get_int ( x : expr ) = let (r, v) = Z3native.get_numeral_int (Expr.gnc x) (Expr.gno x) in @@ -1553,7 +1622,7 @@ struct module Real = struct let mk_sort ( ctx : context ) = - sort_of_ptr ctx (Z3native.mk_real_sort (context_gno ctx)) + Sort.sort_of_ptr ctx (Z3native.mk_real_sort (context_gno ctx)) let get_numerator ( x : expr ) = expr_of_ptr (Expr.gc x) (Z3native.get_numerator (Expr.gnc x) (Expr.gno x)) @@ -1599,14 +1668,14 @@ struct module AlgebraicNumber = struct let to_upper ( x : expr ) ( precision : int ) = - expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_upper (Expr.gnc x) (Expr.gno x) precision) - + expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_upper (Expr.gnc x) (Expr.gno x) precision) + let to_lower ( x : expr ) precision = - expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_lower (Expr.gnc x) (Expr.gno x) precision) - + expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_lower (Expr.gnc x) (Expr.gno x) precision) + let to_decimal_string ( x : expr ) ( precision : int ) = - Z3native.get_numeral_decimal_string (Expr.gnc x) (Expr.gno x) precision - + Z3native.get_numeral_decimal_string (Expr.gnc x) (Expr.gno x) precision + let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) end end @@ -1649,7 +1718,7 @@ end module BitVector = struct let mk_sort ( ctx : context ) size = - sort_of_ptr ctx (Z3native.mk_bv_sort (context_gno ctx) size) + Sort.sort_of_ptr ctx (Z3native.mk_bv_sort (context_gno ctx) size) let is_bv ( x : expr ) = ((sort_kind_of_int (Z3native.get_sort_kind (Expr.gnc x) (Z3native.get_sort (Expr.gnc x) (Expr.gno x)))) == BV_SORT) let is_bv_numeral ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BNUM) @@ -1703,7 +1772,7 @@ struct let is_bv2int ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_BV2INT) let is_bv_carry ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_CARRY) let is_bv_xor3 ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_XOR3) - let get_size (x : sort ) = Z3native.get_bv_sort_size (Sort.gnc x) (Sort.gno x) + let get_size (x : Sort.sort ) = Z3native.get_bv_sort_size (Sort.gnc x) (Sort.gno x) let get_int ( x : expr ) = let (r, v) = Z3native.get_numeral_int (Expr.gnc x) (Expr.gno x) in if r then v @@ -1817,7 +1886,7 @@ struct module RoundingMode = struct let mk_sort ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_rounding_mode_sort (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_rounding_mode_sort (context_gno ctx))) let is_fprm ( x : expr ) = (Sort.get_sort_kind (Expr.get_sort(x))) == ROUNDING_MODE_SORT let mk_round_nearest_ties_to_even ( ctx : context ) = @@ -1843,40 +1912,40 @@ struct end let mk_sort ( ctx : context ) ( ebits : int ) ( sbits : int ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort (context_gno ctx) ebits sbits)) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort (context_gno ctx) ebits sbits)) let mk_sort_half ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort_half (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_half (context_gno ctx))) let mk_sort_16 ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort_16 (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_16 (context_gno ctx))) let mk_sort_single ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort_single (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_single (context_gno ctx))) let mk_sort_32 ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort_32 (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_32 (context_gno ctx))) let mk_sort_double ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort_double (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_double (context_gno ctx))) let mk_sort_64 ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort_64 (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_64 (context_gno ctx))) let mk_sort_quadruple ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort_quadruple (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_quadruple (context_gno ctx))) let mk_sort_128 ( ctx : context ) = - (sort_of_ptr ctx (Z3native.mk_fpa_sort_128 (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_128 (context_gno ctx))) - let mk_nan ( ctx : context ) ( s : sort ) = + let mk_nan ( ctx : context ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_nan (context_gno ctx) (Sort.gno s))) - let mk_inf ( ctx : context ) ( s : sort ) ( negative : bool ) = + let mk_inf ( ctx : context ) ( s : Sort.sort ) ( negative : bool ) = (expr_of_ptr ctx (Z3native.mk_fpa_inf (context_gno ctx) (Sort.gno s) negative)) - let mk_zero ( ctx : context ) ( s : sort ) ( negative : bool ) = + let mk_zero ( ctx : context ) ( s : Sort.sort ) ( negative : bool ) = (expr_of_ptr ctx (Z3native.mk_fpa_zero (context_gno ctx) (Sort.gno s) negative)) let mk_fp ( ctx : context ) ( sign : expr ) ( exponent : expr ) ( significand : expr ) = (expr_of_ptr ctx (Z3native.mk_fpa_fp (context_gno ctx) (Expr.gno sign) (Expr.gno exponent) (Expr.gno significand))) - let mk_numeral_f ( ctx : context ) ( value : float ) ( s : sort ) = + let mk_numeral_f ( ctx : context ) ( value : float ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_numeral_double (context_gno ctx) value (Sort.gno s))) - let mk_numeral_i ( ctx : context ) ( value : int ) ( s : sort ) = + let mk_numeral_i ( ctx : context ) ( value : int ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_numeral_int (context_gno ctx) value (Sort.gno s))) - let mk_numeral_i_u ( ctx : context ) ( sign : bool ) ( exponent : int ) ( significand : int ) ( s : sort ) = + let mk_numeral_i_u ( ctx : context ) ( sign : bool ) ( exponent : int ) ( significand : int ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_numeral_int64_uint64 (context_gno ctx) sign exponent significand (Sort.gno s))) - let mk_numeral_s ( ctx : context ) ( v : string ) ( s : sort ) = + let mk_numeral_s ( ctx : context ) ( v : string ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno s))) let is_fp ( x : expr ) = (Sort.get_sort_kind (Expr.get_sort x)) == FLOATING_POINT_SORT @@ -1912,9 +1981,9 @@ struct let is_to_ieee_bv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_IEEE_BV) let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) - let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( s : sort ) = + let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( s : Sort.sort ) = Expr.mk_const ctx name s - let mk_const_s ( ctx : context ) ( name : string ) ( s : sort ) = + let mk_const_s ( ctx : context ) ( name : string ) ( s : Sort.sort ) = mk_const ctx (Symbol.mk_string ctx name) s let mk_abs ( ctx : context ) ( t : expr ) = @@ -1965,15 +2034,15 @@ struct expr_of_ptr ctx (Z3native.mk_fpa_is_negative (context_gno ctx) (Expr.gno t)) let mk_is_positive ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_is_positive (context_gno ctx) (Expr.gno t)) - let mk_to_fp_bv ( ctx : context ) ( t : expr ) ( s : sort ) = + let mk_to_fp_bv ( ctx : context ) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_bv (context_gno ctx) (Expr.gno t) (Sort.gno s)) - let mk_to_fp_float ( ctx : context ) ( rm : expr) ( t : expr ) ( s : sort ) = + let mk_to_fp_float ( ctx : context ) ( rm : expr) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_float (context_gno ctx) (Expr.gno rm) (Expr.gno t) (Sort.gno s)) - let mk_to_fp_real ( ctx : context ) ( rm : expr ) ( t : expr ) ( s : sort ) = + let mk_to_fp_real ( ctx : context ) ( rm : expr ) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_real (context_gno ctx) (Expr.gno rm) (Expr.gno t) (Sort.gno s)) - let mk_to_fp_signed ( ctx : context ) ( rm : expr) ( t : expr ) ( s : sort ) = + let mk_to_fp_signed ( ctx : context ) ( rm : expr) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_signed (context_gno ctx) (Expr.gno rm) (Expr.gno t) (Sort.gno s)) - let mk_to_fp_unsigned ( ctx : context ) ( rm : expr) ( t : expr ) ( s : sort ) = + let mk_to_fp_unsigned ( ctx : context ) ( rm : expr) ( t : expr ) ( s : Sort.sort ) = expr_of_ptr ctx (Z3native.mk_fpa_to_fp_unsigned (context_gno ctx) (Expr.gno rm) (Expr.gno t) (Sort.gno s)) let mk_to_ubv ( ctx : context ) ( rm : expr) ( t : expr ) ( size : int ) = expr_of_ptr ctx (Z3native.mk_fpa_to_ubv (context_gno ctx) (Expr.gno rm) (Expr.gno t) size) @@ -1982,14 +2051,16 @@ struct let mk_to_real ( ctx : context ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_fpa_to_real (context_gno ctx) (Expr.gno t)) - let get_ebits ( ctx : context ) ( s : sort ) = + let get_ebits ( ctx : context ) ( s : Sort.sort ) = (Z3native.fpa_get_ebits (context_gno ctx) (Sort.gno s)) - let get_sbits ( ctx : context ) ( s : sort ) = + let get_sbits ( ctx : context ) ( s : Sort.sort ) = (Z3native.fpa_get_sbits (context_gno ctx) (Sort.gno s)) let get_numeral_sign ( ctx : context ) ( t : expr ) = (Z3native.fpa_get_numeral_sign (context_gno ctx) (Expr.gno t)) let get_numeral_significand_string ( ctx : context ) ( t : expr ) = (Z3native.fpa_get_numeral_significand_string (context_gno ctx) (Expr.gno t)) + let get_numeral_significand_uint ( ctx : context ) ( t : expr ) = + (Z3native.fpa_get_numeral_significand_uint64 (context_gno ctx) (Expr.gno t)) let get_numeral_exponent_string ( ctx : context ) ( t : expr ) = (Z3native.fpa_get_numeral_exponent_string (context_gno ctx) (Expr.gno t)) let get_numeral_exponent_int ( ctx : context ) ( t : expr ) = @@ -1997,7 +2068,7 @@ struct let mk_to_ieee_bv ( ctx : context ) ( t : expr ) = (expr_of_ptr ctx (Z3native.mk_fpa_to_ieee_bv (context_gno ctx) (Expr.gno t))) - let mk_to_fp_int_real ( ctx : context ) ( rm : expr ) ( exponent : expr ) ( significand : expr ) ( s : sort ) = + let mk_to_fp_int_real ( ctx : context ) ( rm : expr ) ( exponent : expr ) ( significand : expr ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_fpa_to_fp_int_real (context_gno ctx) (Expr.gno rm) (Expr.gno exponent) (Expr.gno significand) (Sort.gno s))) let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) @@ -2128,6 +2199,15 @@ struct create ctx (Z3native.mk_goal (context_gno ctx) models unsat_cores proofs) let to_string ( x : goal ) = Z3native.goal_to_string (z3obj_gnc x) (z3obj_gno x) + + let as_expr ( x : goal ) = + let n = get_size x in + if n = 0 then + (Boolean.mk_true (z3obj_gc x)) + else if n = 1 then + (List.hd (get_formulas x)) + else + (Boolean.mk_and (z3obj_gc x) (get_formulas x)) end @@ -2280,14 +2360,12 @@ struct let get_sorts ( x : model ) = let n = (get_num_sorts x) in - let f i = (sort_of_ptr (z3obj_gc x) (Z3native.model_get_sort (z3obj_gnc x) (z3obj_gno x) i)) in + let f i = (Sort.sort_of_ptr (z3obj_gc x) (Z3native.model_get_sort (z3obj_gnc x) (z3obj_gno x) i)) in mk_list f n - let sort_universe ( x : model ) ( s : sort ) = - let n_univ = AST.ASTVector.create (z3obj_gc x) (Z3native.model_get_sort_universe (z3obj_gnc x) (z3obj_gno x) (Sort.gno s)) in - let n = (AST.ASTVector.get_size n_univ) in - let f i = (AST.ASTVector.get n_univ i) in - mk_list f n + let sort_universe ( x : model ) ( s : Sort.sort ) = + let av = AST.ASTVector.create (z3obj_gc x) (Z3native.model_get_sort_universe (z3obj_gnc x) (z3obj_gno x) (Sort.gno s)) in + (AST.ASTVector.to_expr_list av) let to_string ( x : model ) = Z3native.model_to_string (z3obj_gnc x) (z3obj_gno x) end @@ -2475,6 +2553,91 @@ struct end +module Statistics = +struct + type statistics = z3_native_object + + let create ( ctx : context ) ( no : Z3native.ptr ) = + let res : statistics = { m_ctx = ctx ; + m_n_obj = null ; + inc_ref = Z3native.stats_inc_ref ; + dec_ref = Z3native.stats_dec_ref } in + (z3obj_sno res ctx no) ; + (z3obj_create res) ; + res + + + module Entry = + struct + type statistics_entry = { + mutable m_key : string; + mutable m_is_int : bool ; + mutable m_is_float : bool ; + mutable m_int : int ; + mutable m_float : float } + + let create_si k v = + let res : statistics_entry = { + m_key = k ; + m_is_int = true ; + m_is_float = false ; + m_int = v ; + m_float = 0.0 + } in + res + + let create_sd k v = + let res : statistics_entry = { + m_key = k ; + m_is_int = false ; + m_is_float = true ; + m_int = 0 ; + m_float = v + } in + res + + + let get_key (x : statistics_entry) = x.m_key + let get_int (x : statistics_entry) = x.m_int + let get_float (x : statistics_entry) = x.m_float + let is_int (x : statistics_entry) = x.m_is_int + let is_float (x : statistics_entry) = x.m_is_float + let to_string_value (x : statistics_entry) = + if (is_int x) then + string_of_int (get_int x) + else if (is_float x) then + string_of_float (get_float x) + else + raise (Z3native.Exception "Unknown statistical entry type") + let to_string ( x : statistics_entry ) = (get_key x) ^ ": " ^ (to_string_value x) + end + + let to_string ( x : statistics ) = Z3native.stats_to_string (z3obj_gnc x) (z3obj_gno x) + + let get_size ( x : statistics ) = Z3native.stats_size (z3obj_gnc x) (z3obj_gno x) + + let get_entries ( x : statistics ) = + let n = (get_size x ) in + let f i = ( + let k = Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i in + if (Z3native.stats_is_uint (z3obj_gnc x) (z3obj_gno x) i) then + (Entry.create_si k (Z3native.stats_get_uint_value (z3obj_gnc x) (z3obj_gno x) i)) + else + (Entry.create_sd k (Z3native.stats_get_double_value (z3obj_gnc x) (z3obj_gno x) i)) + ) in + mk_list f n + + let get_keys ( x : statistics ) = + let n = (get_size x) in + let f i = (Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i) in + mk_list f n + + let get ( x : statistics ) ( key : string ) = + let f p c = (if ((Entry.get_key c) == key) then (Some c) else p) in + List.fold_left f None (get_entries x) +end + + module Solver = struct type solver = z3_native_object @@ -2494,90 +2657,6 @@ struct | SATISFIABLE -> "satisfiable" | _ -> "unknown" - module Statistics = - struct - type statistics = z3_native_object - - let create ( ctx : context ) ( no : Z3native.ptr ) = - let res : statistics = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.stats_inc_ref ; - dec_ref = Z3native.stats_dec_ref } in - (z3obj_sno res ctx no) ; - (z3obj_create res) ; - res - - - module Entry = - struct - type statistics_entry = { - mutable m_key : string; - mutable m_is_int : bool ; - mutable m_is_float : bool ; - mutable m_int : int ; - mutable m_float : float } - - let create_si k v = - let res : statistics_entry = { - m_key = k ; - m_is_int = true ; - m_is_float = false ; - m_int = v ; - m_float = 0.0 - } in - res - - let create_sd k v = - let res : statistics_entry = { - m_key = k ; - m_is_int = false ; - m_is_float = true ; - m_int = 0 ; - m_float = v - } in - res - - - let get_key (x : statistics_entry) = x.m_key - let get_int (x : statistics_entry) = x.m_int - let get_float (x : statistics_entry) = x.m_float - let is_int (x : statistics_entry) = x.m_is_int - let is_float (x : statistics_entry) = x.m_is_float - let to_string_value (x : statistics_entry) = - if (is_int x) then - string_of_int (get_int x) - else if (is_float x) then - string_of_float (get_float x) - else - raise (Z3native.Exception "Unknown statistical entry type") - let to_string ( x : statistics_entry ) = (get_key x) ^ ": " ^ (to_string_value x) - end - - let to_string ( x : statistics ) = Z3native.stats_to_string (z3obj_gnc x) (z3obj_gno x) - - let get_size ( x : statistics ) = Z3native.stats_size (z3obj_gnc x) (z3obj_gno x) - - let get_entries ( x : statistics ) = - let n = (get_size x ) in - let f i = ( - let k = Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i in - if (Z3native.stats_is_uint (z3obj_gnc x) (z3obj_gno x) i) then - (Entry.create_si k (Z3native.stats_get_uint_value (z3obj_gnc x) (z3obj_gno x) i)) - else - (Entry.create_sd k (Z3native.stats_get_double_value (z3obj_gnc x) (z3obj_gno x) i)) - ) in - mk_list f n - - let get_keys ( x : statistics ) = - let n = (get_size x) in - let f i = (Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i) in - mk_list f n - - let get ( x : statistics ) ( key : string ) = - let f p c = (if ((Entry.get_key c) == key) then (Some c) else p) in - List.fold_left f None (get_entries x) - end - let get_help ( x : solver ) = Z3native.solver_get_help (z3obj_gnc x) (z3obj_gno x) let set_parameters ( x : solver ) ( p : Params.params )= @@ -2613,10 +2692,8 @@ struct (AST.ASTVector.get_size a) let get_assertions ( x : solver ) = - let a = AST.ASTVector.create (z3obj_gc x) (Z3native.solver_get_assertions (z3obj_gnc x) (z3obj_gno x)) in - let n = (AST.ASTVector.get_size a) in - let f i = (expr_of_ptr (z3obj_gc x) (z3obj_gno (AST.ASTVector.get a i))) in - mk_list f n + let av = AST.ASTVector.create (z3obj_gc x) (Z3native.solver_get_assertions (z3obj_gnc x) (z3obj_gno x)) in + (AST.ASTVector.to_expr_list av) let check ( x : solver ) ( assumptions : expr list ) = let r = @@ -2646,10 +2723,8 @@ struct Some (expr_of_ptr (z3obj_gc x) q) let get_unsat_core ( x : solver ) = - let cn = AST.ASTVector.create (z3obj_gc x) (Z3native.solver_get_unsat_core (z3obj_gnc x) (z3obj_gno x)) in - let n = (AST.ASTVector.get_size cn) in - let f i = (AST.ASTVector.get cn i) in - mk_list f n + let av = AST.ASTVector.create (z3obj_gc x) (Z3native.solver_get_unsat_core (z3obj_gnc x) (z3obj_gno x)) in + (AST.ASTVector.to_expr_list av) let get_reason_unknown ( x : solver ) = Z3native.solver_get_reason_unknown (z3obj_gnc x) (z3obj_gno x) @@ -2691,7 +2766,7 @@ struct let get_help ( x : fixedpoint ) = Z3native.fixedpoint_get_help (z3obj_gnc x) (z3obj_gno x) - let set_params ( x : fixedpoint ) ( p : Params.params )= + let set_parameters ( x : fixedpoint ) ( p : Params.params )= Z3native.fixedpoint_set_params (z3obj_gnc x) (z3obj_gno x) (z3obj_gno p) let get_param_descrs ( x : fixedpoint ) = @@ -2720,7 +2795,7 @@ struct | _ -> Solver.UNKNOWN let query_r ( x : fixedpoint ) ( relations : func_decl list ) = - let f x = ptr_of_ast (ast_of_func_decl x) in + let f x = AST.ptr_of_ast (ast_of_func_decl x) in match (lbool_of_int (Z3native.fixedpoint_query_relations (z3obj_gnc x) (z3obj_gno x) (List.length relations) (Array.of_list (List.map f relations)))) with | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE @@ -2768,21 +2843,112 @@ struct Z3native.fixedpoint_to_string (z3obj_gnc x) (z3obj_gno x) (List.length queries) (Array.of_list (List.map f queries)) let get_rules ( x : fixedpoint ) = - let v = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_get_rules (z3obj_gnc x) (z3obj_gno x))) in - let n = (AST.ASTVector.get_size v) in - let f i =(expr_of_ptr (z3obj_gc x) (z3obj_gno (AST.ASTVector.get v i))) in - mk_list f n + let av = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_get_rules (z3obj_gnc x) (z3obj_gno x))) in + (AST.ASTVector.to_expr_list av) let get_assertions ( x : fixedpoint ) = - let v = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_get_assertions (z3obj_gnc x) (z3obj_gno x))) in - let n = (AST.ASTVector.get_size v) in - let f i =(expr_of_ptr (z3obj_gc x) (z3obj_gno (AST.ASTVector.get v i))) in - mk_list f n + let av = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_get_assertions (z3obj_gnc x) (z3obj_gno x))) in + (AST.ASTVector.to_expr_list av) let mk_fixedpoint ( ctx : context ) = create ctx + + let get_statistics ( x : fixedpoint ) = + let s = Z3native.fixedpoint_get_statistics (z3obj_gnc x) (z3obj_gno x) in + (Statistics.create (z3obj_gc x) s) + + let parse_string ( x : fixedpoint ) ( s : string ) = + let av = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_from_string (z3obj_gnc x) (z3obj_gno x) s)) in + (AST.ASTVector.to_expr_list av) + + let parse_file ( x : fixedpoint ) ( filename : string ) = + let av = (AST.ASTVector.create (z3obj_gc x) (Z3native.fixedpoint_from_file (z3obj_gnc x) (z3obj_gno x) filename)) in + (AST.ASTVector.to_expr_list av) end +module Optimize = +struct + type optimize = z3_native_object + type opt = optimize + type handle = { opt : opt; h : int } + + + let mk_handle (x : opt) h = { opt = x; h = h } + + + let mk_opt (ctx : context) = + let res : opt = { m_ctx = ctx; + m_n_obj = null ; + inc_ref = Z3native.optimize_inc_ref ; + dec_ref = Z3native.optimize_dec_ref } in + (z3obj_sno res ctx (Z3native.mk_optimize (context_gno ctx))) ; + (z3obj_create res) ; + res + + let get_help ( x : opt ) = + Z3native.optimize_get_help (z3obj_gnc x) (z3obj_gno x) + + + let set_parameters ( x : opt ) ( p : Params.params )= + Z3native.optimize_set_params (z3obj_gnc x) (z3obj_gno x) (z3obj_gno p) + + let get_param_descrs ( x : opt ) = + Params.ParamDescrs.param_descrs_of_ptr (z3obj_gc x) (Z3native.optimize_get_param_descrs (z3obj_gnc x) (z3obj_gno x)) + + let add ( x : opt ) ( constraints : expr list ) = + let f e = (Z3native.optimize_assert (z3obj_gnc x) (z3obj_gno x) (Expr.gno e)) in + List.iter f constraints + + + let add_soft ( x : opt ) ( e : Expr.expr) ( w : string ) ( s : Symbol.symbol ) = + mk_handle x (Z3native.optimize_assert_soft (z3obj_gnc x) (z3obj_gno x) (Expr.gno e) w (Symbol.gno s)) + + + let maximize ( x : opt ) ( e : Expr.expr ) = + mk_handle x (Z3native.optimize_maximize (z3obj_gnc x) (z3obj_gno x) (Expr.gno e)) + + + let minimize ( x : opt ) ( e : Expr.expr ) = + mk_handle x (Z3native.optimize_minimize (z3obj_gnc x) (z3obj_gno x) (Expr.gno e)) + + let check ( x : opt ) = + let r = lbool_of_int (Z3native.optimize_check (z3obj_gnc x) (z3obj_gno x)) in + match r with + | L_TRUE -> Solver.SATISFIABLE + | L_FALSE -> Solver.UNSATISFIABLE + | _ -> Solver.UNKNOWN + + + let get_model ( x : opt ) = + let q = Z3native.optimize_get_model (z3obj_gnc x) (z3obj_gno x) in + if (Z3native.is_null q) then + None + else + Some (Model.create (z3obj_gc x) q) + + let get_lower ( x : handle ) ( idx : int ) = + expr_of_ptr (z3obj_gc x.opt) (Z3native.optimize_get_lower (z3obj_gnc x.opt) (z3obj_gno x.opt) idx) + + let get_upper ( x : handle ) ( idx : int ) = + expr_of_ptr (z3obj_gc x.opt) (Z3native.optimize_get_upper (z3obj_gnc x.opt) (z3obj_gno x.opt) idx) + + let push ( x : opt ) = Z3native.optimize_push (z3obj_gnc x) (z3obj_gno x) + + let pop ( x : opt ) = Z3native.optimize_pop (z3obj_gnc x) (z3obj_gno x) + + let get_reason_unknown ( x : opt ) = + Z3native.optimize_get_reason_unknown (z3obj_gnc x) (z3obj_gno x) + + let to_string ( x : opt ) = Z3native.optimize_to_string (z3obj_gnc x) (z3obj_gno x) + + + let get_statistics ( x : opt ) = + let s = Z3native.optimize_get_statistics (z3obj_gnc x) (z3obj_gno x) in + (Statistics.create (z3obj_gc x) s) + + +end + module SMT = struct let benchmark_to_smtstring ( ctx : context ) ( name : string ) ( logic : string ) ( status : string ) ( attributes : string ) ( assumptions : expr list ) ( formula : expr ) = @@ -2790,7 +2956,7 @@ struct (List.length assumptions) (let f x = Expr.gno (x) in (Array.of_list (List.map f assumptions))) (Expr.gno formula) - let parse_smtlib_string ( ctx : context ) ( str : string ) ( sort_names : Symbol.symbol list ) ( sorts : sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = + let parse_smtlib_string ( ctx : context ) ( str : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in let cdn = (List.length decl_names) in @@ -2799,14 +2965,14 @@ struct raise (Z3native.Exception "Argument size mismatch") else Z3native.parse_smtlib_string (context_gno ctx) str - cs - (Symbol.symbol_lton sort_names) - (sort_lton sorts) - cd - (Symbol.symbol_lton decl_names) - (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) + cs + (Symbol.symbol_lton sort_names) + (Sort.sort_lton sorts) + cd + (Symbol.symbol_lton decl_names) + (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) - let parse_smtlib_file ( ctx : context ) ( file_name : string ) ( sort_names : Symbol.symbol list ) ( sorts : sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = + let parse_smtlib_file ( ctx : context ) ( file_name : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in let cdn = (List.length decl_names) in @@ -2815,13 +2981,13 @@ struct raise (Z3native.Exception "Argument size mismatch") else Z3native.parse_smtlib_file (context_gno ctx) file_name - cs - (Symbol.symbol_lton sort_names) - (sort_lton sorts) - cd - (Symbol.symbol_lton decl_names) - (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) - + cs + (Symbol.symbol_lton sort_names) + (Sort.sort_lton sorts) + cd + (Symbol.symbol_lton decl_names) + (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) + let get_num_smtlib_formulas ( ctx : context ) = Z3native.get_smtlib_num_formulas (context_gno ctx) let get_smtlib_formulas ( ctx : context ) = @@ -2847,10 +3013,10 @@ struct let get_smtlib_sorts ( ctx : context ) = let n = (get_num_smtlib_sorts ctx) in - let f i = (sort_of_ptr ctx (Z3native.get_smtlib_sort (context_gno ctx) i)) in + let f i = (Sort.sort_of_ptr ctx (Z3native.get_smtlib_sort (context_gno ctx) i)) in mk_list f n - let parse_smtlib2_string ( ctx : context ) ( str : string ) ( sort_names : Symbol.symbol list ) ( sorts : sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = + let parse_smtlib2_string ( ctx : context ) ( str : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in let cdn = (List.length decl_names) in @@ -2859,14 +3025,14 @@ struct raise (Z3native.Exception "Argument size mismatch") else (expr_of_ptr ctx (Z3native.parse_smtlib2_string (context_gno ctx) str - cs - (Symbol.symbol_lton sort_names) - (sort_lton sorts) - cd - (Symbol.symbol_lton decl_names) - (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) - - let parse_smtlib2_file ( ctx : context ) ( file_name : string ) ( sort_names : Symbol.symbol list ) ( sorts : sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = + cs + (Symbol.symbol_lton sort_names) + (Sort.sort_lton sorts) + cd + (Symbol.symbol_lton decl_names) + (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) + + let parse_smtlib2_file ( ctx : context ) ( file_name : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in let cdn = (List.length decl_names) in @@ -2875,12 +3041,12 @@ struct raise (Z3native.Exception "Argument size mismatch") else (expr_of_ptr ctx (Z3native.parse_smtlib2_string (context_gno ctx) file_name - cs - (Symbol.symbol_lton sort_names) - (sort_lton sorts) - cd - (Symbol.symbol_lton decl_names) - (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) + cs + (Symbol.symbol_lton sort_names) + (Sort.sort_lton sorts) + cd + (Symbol.symbol_lton decl_names) + (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) end module Interpolation = @@ -2902,14 +3068,17 @@ struct res let get_interpolant ( ctx : context ) ( pf : expr ) ( pat: expr ) ( p : Params.params ) = - (ASTVector.create ctx (Z3native.get_interpolant (context_gno ctx) (Expr.gno pf) (Expr.gno pat) (z3obj_gno p))) + let av = (AST.ASTVector.create ctx (Z3native.get_interpolant (context_gno ctx) (Expr.gno pf) (Expr.gno pat) (z3obj_gno p))) in + AST.ASTVector.to_expr_list av let compute_interpolant ( ctx : context ) ( pat : expr ) ( p : Params.params ) = let (r, interp, model) = (Z3native.compute_interpolant (context_gno ctx) (Expr.gno pat) (z3obj_gno p)) in - match (lbool_of_int r) with - | L_TRUE -> ((ASTVector.create ctx interp), (Model.create ctx model)) - | _ -> raise (Z3native.Exception "Error computing interpolant.") - + let res = (lbool_of_int r) in + match res with + | L_TRUE -> (res, None, Some(Model.create ctx model)) + | L_FALSE -> (res, Some((AST.ASTVector.to_expr_list (AST.ASTVector.create ctx interp))), None) + | _ -> (res, None, None) + let get_interpolation_profile ( ctx : context ) = (Z3native.interpolation_profile (context_gno ctx)) @@ -2965,3 +3134,4 @@ let enable_trace ( tag : string ) = let disable_trace ( tag : string ) = (Z3native.enable_trace tag) + diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index db0c3f130..dba15c85d 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -123,7 +123,7 @@ sig end (** The abstract syntax tree (AST) module *) -module AST : +module rec AST : sig type ast @@ -156,6 +156,12 @@ sig @return A new ASTVector *) val translate : ast_vector -> context -> ast_vector + (** Translates the ASTVector into an (Ast.ast list) *) + val to_list : ast_vector -> ast list + + (** Translates the ASTVector into an (Expr.expr list) *) + val to_expr_list : ast_vector -> Expr.expr list + (** Retrieves a string representation of the vector. *) val to_string : ast_vector -> string end @@ -260,7 +266,7 @@ sig end (** The Sort module implements type information for ASTs *) -module Sort : +and Sort : sig type sort = Sort of AST.ast @@ -291,7 +297,7 @@ sig end (** Function declarations *) -module rec FuncDecl : +and FuncDecl : sig type func_decl = FuncDecl of AST.ast @@ -2155,6 +2161,12 @@ sig (** Return the significand value of a floating-point numeral as a string. *) val get_numeral_significand_string : context -> Expr.expr -> string + (** Return the significand value of a floating-point numeral as a uint64. + Remark: This function extracts the significand bits, without the + hidden bit or normalization. Throws an exception if the + significand does not fit into a uint64. *) + val get_numeral_significand_uint : context -> Expr.expr -> bool * int + (** Return the exponent value of a floating-point numeral as a string *) val get_numeral_exponent_string : context -> Expr.expr -> string @@ -2641,6 +2653,9 @@ sig (** A string representation of the Goal. *) val to_string : goal -> string + + (** Goal to BoolExpr conversion. *) + val as_expr : goal -> Expr.expr end (** Models @@ -2751,7 +2766,7 @@ sig (** The finite set of distinct values that represent the interpretation of a sort. {!get_sorts} @return A list of expressions, where each is an element of the universe of the sort *) - val sort_universe : model -> Sort.sort -> AST.ast list + val sort_universe : model -> Sort.sort -> Expr.expr list (** Conversion of models to strings. @return A string representation of the model. *) @@ -2938,6 +2953,55 @@ sig val interrupt : context -> unit end +(** Objects that track statistical information. *) +module Statistics : +sig + type statistics + + (** Statistical data is organized into pairs of \[Key, Entry\], where every + Entry is either a floating point or integer value. *) + module Entry : + sig + type statistics_entry + + (** The key of the entry. *) + val get_key : statistics_entry -> string + + (** The int-value of the entry. *) + val get_int : statistics_entry -> int + + (** The float-value of the entry. *) + val get_float : statistics_entry -> float + + (** True if the entry is uint-valued. *) + val is_int : statistics_entry -> bool + + (** True if the entry is float-valued. *) + val is_float : statistics_entry -> bool + + (** The string representation of the the entry's value. *) + val to_string_value : statistics_entry -> string + + (** The string representation of the entry (key and value) *) + val to_string : statistics_entry -> string + end + + (** A string representation of the statistical data. *) + val to_string : statistics -> string + + (** The number of statistical data. *) + val get_size : statistics -> int + + (** The data entries. *) + val get_entries : statistics -> Entry.statistics_entry list + + (** The statistical counters. *) + val get_keys : statistics -> string list + + (** The value of a particular statistical counter. *) + val get : statistics -> string -> Entry.statistics_entry option +end + (** Solvers *) module Solver : sig @@ -2946,56 +3010,6 @@ sig val string_of_status : status -> string - (** Objects that track statistical information about solvers. *) - module Statistics : - sig - type statistics - - (** Statistical data is organized into pairs of \[Key, Entry\], where every - Entry is either a floating point or integer value. - *) - module Entry : - sig - type statistics_entry - - (** The key of the entry. *) - val get_key : statistics_entry -> string - - (** The int-value of the entry. *) - val get_int : statistics_entry -> int - - (** The float-value of the entry. *) - val get_float : statistics_entry -> float - - (** True if the entry is uint-valued. *) - val is_int : statistics_entry -> bool - - (** True if the entry is float-valued. *) - val is_float : statistics_entry -> bool - - (** The string representation of the the entry's value. *) - val to_string_value : statistics_entry -> string - - (** The string representation of the entry (key and value) *) - val to_string : statistics_entry -> string - end - - (** A string representation of the statistical data. *) - val to_string : statistics -> string - - (** The number of statistical data. *) - val get_size : statistics -> int - - (** The data entries. *) - val get_entries : statistics -> Entry.statistics_entry list - - (** The statistical counters. *) - val get_keys : statistics -> string list - - (** The value of a particular statistical counter. *) - val get : statistics -> string -> Entry.statistics_entry option - end - (** A string that describes all available solver parameters. *) val get_help : solver -> string @@ -3081,7 +3095,7 @@ sig The unsat core is a subset of [Assertions] The result is empty if [Check] was not invoked before, if its results was not [UNSATISFIABLE], or if core production is disabled. *) - val get_unsat_core : solver -> AST.ast list + val get_unsat_core : solver -> Expr.expr list (** A brief justification of why the last call to [Check] returned [UNKNOWN]. *) val get_reason_unknown : solver -> string @@ -3122,7 +3136,7 @@ sig val get_help : fixedpoint -> string (** Sets the fixedpoint solver parameters. *) - val set_params : fixedpoint -> Params.params -> unit + val set_parameters : fixedpoint -> Params.params -> unit (** Retrieves parameter descriptions for Fixedpoint solver. *) val get_param_descrs : fixedpoint -> Params.ParamDescrs.param_descrs @@ -3198,8 +3212,104 @@ sig (** Create a Fixedpoint context. *) val mk_fixedpoint : context -> fixedpoint + + (** Retrieve statistics information from the last call to #Z3_fixedpoint_query. *) + val get_statistics : fixedpoint -> Statistics.statistics + + (** Parse an SMT-LIB2 string with fixedpoint rules. + Add the rules to the current fixedpoint context. + Return the set of queries in the string. *) + val parse_string : fixedpoint -> string -> Expr.expr list + + (** Parse an SMT-LIB2 file with fixedpoint rules. + Add the rules to the current fixedpoint context. + Return the set of queries in the file. *) + val parse_file : fixedpoint -> string -> Expr.expr list end +(** Optimization *) +module Optimize : +sig + type optimize + type handle + + + (** Create a Optimize context. *) + val mk_opt : context -> optimize + + (** A string that describes all available optimize solver parameters. *) + val get_help : optimize -> string + + + (** Sets the optimize solver parameters. *) + val set_parameters : optimize -> Params.params -> unit + + + (** Retrieves parameter descriptions for Optimize solver. *) + val get_param_descrs : optimize -> Params.ParamDescrs.param_descrs + + + (** Assert a constraints into the optimize solver. *) + val add : optimize -> Expr.expr list -> unit + + + (** Asssert a soft constraint. + Supply integer weight and string that identifies a group + of soft constraints. + *) + val add_soft : optimize -> Expr.expr -> string -> Symbol.symbol -> handle + + + (** Add maximization objective. + *) + val maximize : optimize -> Expr.expr -> handle + + + (** Add minimization objective. + *) + val minimize : optimize -> Expr.expr -> handle + + (** Checks whether the assertions in the context are satisfiable and solves objectives. + *) + val check : optimize -> Solver.status + + + (** Retrieve model from satisfiable context *) + val get_model : optimize -> Model.model option + + + (** Retrieve lower bound in current model for handle *) + val get_lower : handle -> int -> Expr.expr + + + (** Retrieve upper bound in current model for handle *) + val get_upper : handle -> int -> Expr.expr + + + (** Creates a backtracking point. + {!pop} *) + val push : optimize -> unit + + + (** Backtrack one backtracking point. + Note that an exception is thrown if Pop is called without a corresponding [Push] + {!push} *) + val pop : optimize -> unit + + + (** Retrieve explanation why optimize engine returned status Unknown. *) + val get_reason_unknown : optimize -> string + + + (** Retrieve SMT-LIB string representation of optimize object. *) + val to_string : optimize -> string + + + (** Retrieve statistics information from the last call to check *) + val get_statistics : optimize -> Statistics.statistics +end + + (** Functions for handling SMT and SMT2 expressions and files *) module SMT : sig @@ -3272,12 +3382,12 @@ sig (** Gets an interpolant. For more information on interpolation please refer too the C/C++ API, which is well documented. *) - val get_interpolant : context -> Expr.expr -> Expr.expr -> Params.params -> AST.ASTVector.ast_vector + val get_interpolant : context -> Expr.expr -> Expr.expr -> Params.params -> Expr.expr list (** Computes an interpolant. For more information on interpolation please refer too the C/C++ API, which is well documented. *) - val compute_interpolant : context -> Expr.expr -> Params.params -> (AST.ASTVector.ast_vector * Model.model) + val compute_interpolant : context -> Expr.expr -> Params.params -> (Z3enums.lbool * Expr.expr list option * Model.model option) (** Retrieves an interpolation profile. For more information on interpolation please refer @@ -3355,3 +3465,5 @@ val enable_trace : string -> unit *) val disable_trace : string -> unit + + diff --git a/src/api/python/z3.py b/src/api/python/z3.py index ce1cc2103..5763db916 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -32,7 +32,7 @@ sat Z3 exceptions: >>> try: -... x = Int('x') +... x = BitVec('x', 32) ... y = Bool('y') ... # the expression x + y is type incorrect ... n = x + y @@ -301,7 +301,6 @@ class AstRef(Z3PPObject): """Return unique identifier for object. It can be used for hash-tables and maps.""" return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) - def ctx_ref(self): """Return a reference to the C context where this AST node is stored.""" return self.ctx.ref() @@ -455,7 +454,6 @@ class SortRef(AstRef): def get_id(self): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) - def kind(self): """Return the Z3 internal kind of a sort. This method can be used to test if `self` is one of the Z3 builtin sorts. @@ -555,6 +553,8 @@ def _to_sort_ref(s, ctx): return ArraySortRef(s, ctx) elif k == Z3_DATATYPE_SORT: return DatatypeSortRef(s, ctx) + elif k == Z3_FINITE_DOMAIN_SORT: + return FiniteDomainSortRef(s, ctx) elif k == Z3_FLOATING_POINT_SORT: return FPSortRef(s, ctx) elif k == Z3_ROUNDING_MODE_SORT: @@ -1228,6 +1228,16 @@ class BoolSortRef(SortRef): _z3_assert(self.eq(val.sort()), "Value cannot be converted into a Z3 Boolean value") return val + def subsort(self, other): + return isinstance(other, ArithSortRef) + + def is_int(self): + return True + + def is_bool(self): + return True + + class BoolRef(ExprRef): """All Boolean expressions are instances of this class.""" def sort(self): @@ -1900,6 +1910,10 @@ class ArithSortRef(SortRef): return val if val_s.is_int() and self.is_real(): return ToReal(val) + if val_s.is_bool() and self.is_int(): + return If(val, 1, 0) + if val_s.is_bool() and self.is_real(): + return ToReal(If(val, 1, 0)) if __debug__: _z3_assert(False, "Z3 Integer/Real expression expected" ) else: @@ -3906,6 +3920,10 @@ class ArrayRef(ExprRef): arg = self.domain().cast(arg) return _to_expr_ref(Z3_mk_select(self.ctx_ref(), self.as_ast(), arg.as_ast()), self.ctx) + def mk_default(self): + return _to_expr_ref(Z3_mk_array_default(self.ctx_ref(), self.as_ast()), self.ctx) + + def is_array(a): """Return `True` if `a` is a Z3 array expression. @@ -3958,6 +3976,14 @@ def is_map(a): """ return is_app_of(a, Z3_OP_ARRAY_MAP) +def is_default(a): + """Return `True` if `a` is a Z3 default array expression. + >>> d = Default(K(IntSort(), 10)) + >>> is_default(d) + True + """ + return is_app_of(a, Z3_OP_ARRAY_DEFAULT) + def get_map_func(a): """Return the function declaration associated with a Z3 map array expression. @@ -4030,6 +4056,17 @@ def Update(a, i, v): ctx = a.ctx return _to_expr_ref(Z3_mk_store(ctx.ref(), a.as_ast(), i.as_ast(), v.as_ast()), ctx) +def Default(a): + """ Return a default value for array expression. + >>> b = K(IntSort(), 1) + >>> prove(Default(b) == 1) + proved + """ + if __debug__: + _z3_assert(is_array(a), "First argument must be a Z3 array expression") + return a.mk_default() + + def Store(a, i, v): """Return a Z3 store array expression. @@ -5603,7 +5640,7 @@ class Statistics: sat >>> st = s.statistics() >>> len(st) - 2 + 6 """ return int(Z3_stats_size(self.ctx.ref(), self.stats)) @@ -5617,7 +5654,7 @@ class Statistics: sat >>> st = s.statistics() >>> len(st) - 2 + 6 >>> st[0] ('nlsat propagations', 2) >>> st[1] @@ -5641,7 +5678,7 @@ class Statistics: sat >>> st = s.statistics() >>> st.keys() - ['nlsat propagations', 'nlsat stages'] + ['nlsat propagations', 'nlsat stages', 'rlimit count', 'max memory', 'memory', 'num allocs'] """ return [Z3_stats_get_key(self.ctx.ref(), self.stats, idx) for idx in range(len(self))] @@ -5678,7 +5715,7 @@ class Statistics: sat >>> st = s.statistics() >>> st.keys() - ['nlsat propagations', 'nlsat stages'] + ['nlsat propagations', 'nlsat stages', 'rlimit count', 'max memory', 'memory', 'num allocs'] >>> st.nlsat_propagations 2 >>> st.nlsat_stages @@ -6071,8 +6108,6 @@ class Solver(Z3PPObject): e = BoolVal(True, self.ctx).as_ast() return Z3_benchmark_to_smtlib_string(self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e) - - def SolverFor(logic, ctx=None): """Create a solver customized for the given logic. @@ -6333,6 +6368,170 @@ class Fixedpoint(Z3PPObject): else: return Exists(self.vars, fml) + +######################################### +# +# Finite domain sorts +# +######################################### + +class FiniteDomainSortRef(SortRef): + """Finite domain sort.""" + + def size(self): + """Return the size of the finite domain sort""" + r = (ctype.c_ulonglong * 1)() + if Z3_get_finite_domain_sort_size(self.ctx_ref(), self.ast(), r): + return r[0] + else: + raise Z3Exception("Failed to retrieve finite domain sort size") + +def FiniteDomainSort(name, sz, ctx=None): + """Create a named finite domain sort of a given size sz""" + ctx = _get_ctx(ctx) + return FiniteDomainSortRef(Z3_mk_finite_domain_sort(ctx.ref(), name, sz), ctx) + +######################################### +# +# Optimize +# +######################################### + +class OptimizeObjective: + def __init__(self, opt, value, is_max): + self._opt = opt + self._value = value + self._is_max = is_max + + def lower(self): + opt = self._opt + return _to_expr_ref(Z3_optimize_get_lower(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) + + def upper(self): + opt = self._opt + return _to_expr_ref(Z3_optimize_get_upper(opt.ctx.ref(), opt.optimize, self._value), opt.ctx) + + def value(self): + if self._is_max: + return self.upper() + else: + return self.lower() + +class Optimize(Z3PPObject): + """Optimize API provides methods for solving using objective functions and weighted soft constraints""" + + def __init__(self, ctx=None): + self.ctx = _get_ctx(ctx) + self.optimize = Z3_mk_optimize(self.ctx.ref()) + Z3_optimize_inc_ref(self.ctx.ref(), self.optimize) + + def __del__(self): + if self.optimize != None: + Z3_optimize_dec_ref(self.ctx.ref(), self.optimize) + + def set(self, *args, **keys): + """Set a configuration option. The method `help()` return a string containing all available options. + """ + p = args2params(args, keys, self.ctx) + Z3_optimize_set_params(self.ctx.ref(), self.optimize, p.params) + + def help(self): + """Display a string describing all available options.""" + print(Z3_optimize_get_help(self.ctx.ref(), self.optimize)) + + def param_descrs(self): + """Return the parameter description set.""" + return ParamDescrsRef(Z3_optimize_get_param_descrs(self.ctx.ref(), self.optimize), self.ctx) + + def assert_exprs(self, *args): + """Assert constraints as background axioms for the optimize solver.""" + args = _get_args(args) + for arg in args: + if isinstance(arg, Goal) or isinstance(arg, AstVector): + for f in arg: + Z3_optimize_assert(self.ctx.ref(), self.optimize, f.as_ast()) + else: + Z3_optimize_assert(self.ctx.ref(), self.optimize, arg.as_ast()) + + def add(self, *args): + """Assert constraints as background axioms for the optimize solver. Alias for assert_expr.""" + self.assert_exprs(*args) + + def add_soft(self, arg, weight = "1", id = None): + """Add soft constraint with optional weight and optional identifier. + If no weight is supplied, then the penalty for violating the soft constraint + is 1. + Soft constraints are grouped by identifiers. Soft constraints that are + added without identifiers are grouped by default. + """ + if _is_int(weight): + weight = "%d" % weight + if not isinstance(weight, str): + raise Z3Exception("weight should be a string or an integer") + if id == None: + id = "" + id = to_symbol(id, self.ctx) + v = Z3_optimize_assert_soft(self.ctx.ref(), self.optimize, arg.as_ast(), weight, id) + return OptimizeObjective(self, v, False) + + def maximize(self, arg): + """Add objective function to maximize.""" + return OptimizeObjective(self, Z3_optimize_maximize(self.ctx.ref(), self.optimize, arg.as_ast()), True) + + def minimize(self, arg): + """Add objective function to minimize.""" + return OptimizeObjective(self, Z3_optimize_minimize(self.ctx.ref(), self.optimize, arg.as_ast()), False) + + def push(self): + """create a backtracking point for added rules, facts and assertions""" + Z3_optimize_push(self.ctx.ref(), self.optimize) + + def pop(self): + """restore to previously created backtracking point""" + Z3_optimize_pop(self.ctx.ref(), self.optimize) + + def check(self): + """Check satisfiability while optimizing objective functions.""" + return CheckSatResult(Z3_optimize_check(self.ctx.ref(), self.optimize)) + + def reason_unknown(self): + """Return a string that describes why the last `check()` returned `unknown`.""" + return Z3_optimize_get_reason_unknown(self.ctx.ref(), self.optimize) + + def model(self): + """Return a model for the last check().""" + try: + return ModelRef(Z3_optimize_get_model(self.ctx.ref(), self.optimize), self.ctx) + except Z3Exception: + raise Z3Exception("model is not available") + + def lower(self, obj): + if not isinstance(obj, OptimizeObjective): + raise Z3Exception("Expecting objective handle returned by maximize/minimize") + return obj.lower() + + def upper(self, obj): + if not isinstance(obj, OptimizeObjective): + raise Z3Exception("Expecting objective handle returned by maximize/minimize") + return obj.upper() + + def __repr__(self): + """Return a formatted string with all added rules and constraints.""" + return self.sexpr() + + def sexpr(self): + """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. + """ + return Z3_optimize_to_string(self.ctx.ref(), self.optimize) + + def statistics(self): + """Return statistics for the last `query()`. + """ + return Statistics(Z3_optimize_get_statistics(self.ctx.ref(), self.optimize), self.ctx) + + + + ######################################### # # ApplyResult @@ -8022,23 +8221,24 @@ def FP(name, fpsort, ctx=None): >>> eq(x, x2) True """ - ctx = fpsort.ctx + if isinstance(fpsort, FPSortRef): + ctx = fpsort.ctx + else: + ctx = _get_ctx(ctx) return FPRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), fpsort.ast), ctx) def FPs(names, fpsort, ctx=None): """Return an array of floating-point constants. - >>> x, y, z = BitVecs('x y z', 16) - >>> x.size() - 16 + >>> x, y, z = FPs('x y z', FPSort(8, 24)) >>> x.sort() - BitVec(16) - >>> Sum(x, y, z) - 0 + x + y + z - >>> Product(x, y, z) - 1*x*y*z - >>> simplify(Product(x, y, z)) - x*y*z + FPSort(8, 24) + >>> x.sbits() + 24 + >>> x.ebits() + 8 + >>> fpMul(RNE(), fpAdd(RNE(), x, y), z) + fpMul(RNE(), fpAdd(RNE(), x, y), z) """ ctx = z3._get_ctx(ctx) if isinstance(names, str): diff --git a/src/api/python/z3printer.py b/src/api/python/z3printer.py index 2cc29bd5d..fe6378357 100644 --- a/src/api/python/z3printer.py +++ b/src/api/python/z3printer.py @@ -1017,6 +1017,8 @@ class Formatter: return self.pp_seq(a.assertions(), 0, []) elif isinstance(a, z3.Fixedpoint): return a.sexpr() + elif isinstance(a, z3.Optimize): + return a.sexpr() elif isinstance(a, z3.ApplyResult): return self.pp_seq_seq(a, 0, []) elif isinstance(a, z3.ModelRef): diff --git a/src/api/python/z3test.py b/src/api/python/z3test.py index 9b894d7ad..38939cebf 100644 --- a/src/api/python/z3test.py +++ b/src/api/python/z3test.py @@ -1,3 +1,10 @@ +############################################ +# Copyright (c) 2012 Microsoft Corporation +# +# Z3 Python interface +# +# Author: Leonardo de Moura (leonardo) +############################################ import z3, doctest r = doctest.testmod(z3) diff --git a/src/api/python/z3types.py b/src/api/python/z3types.py index 5d59368ff..44b19d33a 100644 --- a/src/api/python/z3types.py +++ b/src/api/python/z3types.py @@ -1,3 +1,11 @@ +############################################ +# Copyright (c) 2012 Microsoft Corporation +# +# Z3 Python interface +# +# Author: Leonardo de Moura (leonardo) +############################################ + import ctypes, z3core class Z3Exception(Exception): @@ -78,6 +86,10 @@ class FixedpointObj(ctypes.c_void_p): def __init__(self, fixedpoint): self._as_parameter_ = fixedpoint def from_param(obj): return obj +class OptimizeObj(ctypes.c_void_p): + def __init__(self, optimize): self._as_parameter_ = optimize + def from_param(obj): return obj + class ModelObj(ctypes.c_void_p): def __init__(self, model): self._as_parameter_ = model def from_param(obj): return obj diff --git a/src/api/python/z3util.py b/src/api/python/z3util.py index 38d345cf6..2494461cf 100644 --- a/src/api/python/z3util.py +++ b/src/api/python/z3util.py @@ -1,9 +1,22 @@ +############################################ +# Copyright (c) 2012 Microsoft Corporation +# +# Z3 Python interface +# +# Authors: Leonardo de Moura (leonardo) +# ThanhVu (Vu) Nguyen +############################################ +""" +Usage: +import common_z3 as CM_Z3 +""" from z3 import * def vset(seq, idfun=None, as_list=True): + # This functions preserves the order of arguments while removing duplicates. # This function is from https://code.google.com/p/common-python-vu/source/browse/vu_common.py - # It preserves the order of arguments while removing duplicates. + # (Thanhu's personal code). It has been copied here to avoid a dependency on vu_common.py. """ order preserving @@ -485,7 +498,7 @@ def model_str(m,as_str=True): if m : vs = [(v,m[v]) for v in m] - vs = sorted(vs,key=lambda a: str(a[0])) + vs = sorted(vs,key=lambda a,_: str(a)) if as_str: return '\n'.join(['{} = {}'.format(k,v) for (k,v) in vs]) else: diff --git a/src/api/z3.h b/src/api/z3.h index bb7611030..b56a2f7a8 100644 --- a/src/api/z3.h +++ b/src/api/z3.h @@ -18,8 +18,8 @@ Notes: --*/ -#ifndef _Z3__H_ -#define _Z3__H_ +#ifndef Z3_H_ +#define Z3_H_ #include #include"z3_macros.h" @@ -30,15 +30,5 @@ Notes: #include"z3_interp.h" #include"z3_fpa.h" -#undef __in -#undef __out -#undef __inout -#undef __in_z -#undef __out_z -#undef __ecount -#undef __in_ecount -#undef __out_ecount -#undef __inout_ecount - #endif diff --git a/src/api/z3_algebraic.h b/src/api/z3_algebraic.h index 8b6d97aa8..5e4762551 100644 --- a/src/api/z3_algebraic.h +++ b/src/api/z3_algebraic.h @@ -18,8 +18,8 @@ Notes: --*/ -#ifndef _Z3_ALGEBRAIC_H_ -#define _Z3_ALGEBRAIC_H_ +#ifndef Z3_ALGEBRAIC_H_ +#define Z3_ALGEBRAIC_H_ #ifdef __cplusplus extern "C" { @@ -43,7 +43,7 @@ extern "C" { def_API('Z3_algebraic_is_value', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_is_value(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_algebraic_is_value(Z3_context c, Z3_ast a); /** \brief Return the Z3_TRUE if \c a is positive, and Z3_FALSE otherwise. @@ -52,7 +52,7 @@ extern "C" { def_API('Z3_algebraic_is_pos', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_is_pos(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_algebraic_is_pos(Z3_context c, Z3_ast a); /** \brief Return the Z3_TRUE if \c a is negative, and Z3_FALSE otherwise. @@ -61,7 +61,7 @@ extern "C" { def_API('Z3_algebraic_is_neg', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_is_neg(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_algebraic_is_neg(Z3_context c, Z3_ast a); /** \brief Return the Z3_TRUE if \c a is zero, and Z3_FALSE otherwise. @@ -70,7 +70,7 @@ extern "C" { def_API('Z3_algebraic_is_zero', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_is_zero(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_algebraic_is_zero(Z3_context c, Z3_ast a); /** \brief Return 1 if \c a is positive, 0 if \c a is zero, and -1 if \c a is negative. @@ -79,7 +79,7 @@ extern "C" { def_API('Z3_algebraic_sign', INT, (_in(CONTEXT), _in(AST))) */ - int Z3_API Z3_algebraic_sign(__in Z3_context c, __in Z3_ast a); + int Z3_API Z3_algebraic_sign(Z3_context c, Z3_ast a); /** \brief Return the value a + b. @@ -90,7 +90,7 @@ extern "C" { def_API('Z3_algebraic_add', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_algebraic_add(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_ast Z3_API Z3_algebraic_add(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a - b. @@ -101,7 +101,7 @@ extern "C" { def_API('Z3_algebraic_sub', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_algebraic_sub(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a * b. @@ -112,7 +112,7 @@ extern "C" { def_API('Z3_algebraic_mul', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_algebraic_mul(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the value a / b. @@ -124,7 +124,7 @@ extern "C" { def_API('Z3_algebraic_div', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_algebraic_div(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return the a^(1/k) @@ -135,7 +135,7 @@ extern "C" { def_API('Z3_algebraic_root', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_ast Z3_API Z3_algebraic_root(__in Z3_context c, __in Z3_ast a, __in unsigned k); + Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k); /** \brief Return the a^k @@ -145,7 +145,7 @@ extern "C" { def_API('Z3_algebraic_power', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_ast Z3_API Z3_algebraic_power(__in Z3_context c, __in Z3_ast a, __in unsigned k); + Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k); /** \brief Return Z3_TRUE if a < b, and Z3_FALSE otherwise. @@ -155,7 +155,7 @@ extern "C" { def_API('Z3_algebraic_lt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_lt(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_bool Z3_API Z3_algebraic_lt(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a > b, and Z3_FALSE otherwise. @@ -165,7 +165,7 @@ extern "C" { def_API('Z3_algebraic_gt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_gt(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_bool Z3_API Z3_algebraic_gt(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a <= b, and Z3_FALSE otherwise. @@ -175,7 +175,7 @@ extern "C" { def_API('Z3_algebraic_le', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_le(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_bool Z3_API Z3_algebraic_le(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a >= b, and Z3_FALSE otherwise. @@ -185,7 +185,7 @@ extern "C" { def_API('Z3_algebraic_ge', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_ge(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_bool Z3_API Z3_algebraic_ge(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a == b, and Z3_FALSE otherwise. @@ -195,7 +195,7 @@ extern "C" { def_API('Z3_algebraic_eq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_eq(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_bool Z3_API Z3_algebraic_eq(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Return Z3_TRUE if a != b, and Z3_FALSE otherwise. @@ -205,7 +205,7 @@ extern "C" { def_API('Z3_algebraic_neq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_bool Z3_API Z3_algebraic_neq(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + Z3_bool Z3_API Z3_algebraic_neq(Z3_context c, Z3_ast a, Z3_ast b); /** \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the @@ -217,7 +217,7 @@ extern "C" { def_API('Z3_algebraic_roots', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ - Z3_ast_vector Z3_API Z3_algebraic_roots(__in Z3_context c, __in Z3_ast p, __in unsigned n, __in Z3_ast a[]); + Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]); /** \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}), return the @@ -228,7 +228,7 @@ extern "C" { def_API('Z3_algebraic_eval', INT, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ - int Z3_API Z3_algebraic_eval(__in Z3_context c, __in Z3_ast p, __in unsigned n, __in Z3_ast a[]); + int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]); /*@}*/ /*@}*/ diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 35b2206ac..212fa12fd 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1,5 +1,11 @@ -#ifndef _Z3_API_H_ -#define _Z3_API_H_ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#ifndef Z3_API_H_ +#define Z3_API_H_ #ifdef CAMLIDL #ifdef MLAPIV3 @@ -47,6 +53,7 @@ DEFINE_TYPE(Z3_func_interp); #define Z3_func_interp_opt Z3_func_interp DEFINE_TYPE(Z3_func_entry); DEFINE_TYPE(Z3_fixedpoint); +DEFINE_TYPE(Z3_optimize); DEFINE_TYPE(Z3_rcf_num); DEFINE_VOID(Z3_theory_data); #endif @@ -85,6 +92,7 @@ DEFINE_VOID(Z3_theory_data); - \c Z3_func_interp: interpretation of a function in a model. - \c Z3_func_entry: representation of the value of a \c Z3_func_interp at a particular point. - \c Z3_fixedpoint: context for the recursive predicate solver. + - \c Z3_optimize: context for solving optimization queries. - \c Z3_ast_vector: vector of \c Z3_ast objects. - \c Z3_ast_map: mapping from \c Z3_ast to \c Z3_ast objects. - \c Z3_goal: set of formulas that can be solved and/or transformed using tactics and solvers. @@ -592,7 +600,10 @@ typedef enum } This proof object has one antecedent: a hypothetical proof for false. It converts the proof in a proof for (or (not l_1) ... (not l_n)), - when T1 contains the hypotheses: l_1, ..., l_n. + when T1 contains the open hypotheses: l_1, ..., l_n. + The hypotheses are closed after an application of a lemma. + Furthermore, there are no other open hypotheses in the subtree covered by + the lemma. - Z3_OP_PR_UNIT_RESOLUTION: \nicebox{ @@ -877,6 +888,17 @@ typedef enum - Z3_OP_DT_ACCESSOR: datatype accessor. + - Z3_OP_DT_UPDATE_FIELD: datatype field update. + + - Z3_OP_PB_AT_MOST: Cardinality constraint. + E.g., x + y + z <= 2 + + - Z3_OP_PB_LE: Generalized Pseudo-Boolean cardinality constraint. + Example 2*x + 3*y <= 4 + + - Z3_OP_PB_GE: Generalized Pseudo-Boolean cardinality constraint. + Example 2*x + 3*y + 2*z >= 4 + - Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN: Floating-point rounding mode RNE - Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: Floating-point rounding mode RNA @@ -1141,6 +1163,12 @@ typedef enum { Z3_OP_DT_CONSTRUCTOR=0x800, Z3_OP_DT_RECOGNISER, Z3_OP_DT_ACCESSOR, + Z3_OP_DT_UPDATE_FIELD, + + // Pseudo Booleans + Z3_OP_PB_AT_MOST=0x900, + Z3_OP_PB_LE, + Z3_OP_PB_GE, // Floating-Point Arithmetic Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN, @@ -1327,6 +1355,7 @@ typedef enum def_Type('FUNC_INTERP', 'Z3_func_interp', 'FuncInterpObj') def_Type('FUNC_ENTRY', 'Z3_func_entry', 'FuncEntryObj') def_Type('FIXEDPOINT', 'Z3_fixedpoint', 'FixedpointObj') + def_Type('OPTIMIZE', 'Z3_optimize', 'OptimizeObj') def_Type('PARAM_DESCRS', 'Z3_param_descrs', 'ParamDescrs') def_Type('RCF_NUM', 'Z3_rcf_num', 'RCFNumObj') */ @@ -1400,7 +1429,7 @@ extern "C" { def_API('Z3_global_param_set', VOID, (_in(STRING), _in(STRING))) */ - void Z3_API Z3_global_param_set(__in Z3_string param_id, __in Z3_string param_value); + void Z3_API Z3_global_param_set(Z3_string param_id, Z3_string param_value); /** @@ -1426,7 +1455,7 @@ extern "C" { def_API('Z3_global_param_get', BOOL, (_in(STRING), _out(STRING))) */ - Z3_bool_opt Z3_API Z3_global_param_get(__in Z3_string param_id, __out_opt Z3_string_ptr param_value); + Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value); /*@}*/ @@ -1479,7 +1508,7 @@ extern "C" { def_API('Z3_del_config', VOID, (_in(CONFIG),)) */ - void Z3_API Z3_del_config(__in Z3_config c); + void Z3_API Z3_del_config(Z3_config c); /** \brief Set a configuration parameter. @@ -1490,7 +1519,7 @@ extern "C" { def_API('Z3_set_param_value', VOID, (_in(CONFIG), _in(STRING), _in(STRING))) */ - void Z3_API Z3_set_param_value(__in Z3_config c, __in Z3_string param_id, __in Z3_string param_value); + void Z3_API Z3_set_param_value(Z3_config c, Z3_string param_id, Z3_string param_value); /*@}*/ #endif @@ -1524,7 +1553,7 @@ extern "C" { def_API('Z3_mk_context', CONTEXT, (_in(CONFIG),)) */ #ifdef CorML3 - Z3_context Z3_API Z3_mk_context(__in Z3_config c); + Z3_context Z3_API Z3_mk_context(Z3_config c); #endif #ifdef ML4only #include @@ -1551,7 +1580,7 @@ extern "C" { def_API('Z3_mk_context_rc', CONTEXT, (_in(CONFIG),)) */ - Z3_context Z3_API Z3_mk_context_rc(__in Z3_config c); + Z3_context Z3_API Z3_mk_context_rc(Z3_config c); #endif #ifdef CorML3 @@ -1562,7 +1591,7 @@ extern "C" { def_API('Z3_del_context', VOID, (_in(CONTEXT),)) */ - void Z3_API Z3_del_context(__in Z3_context c); + void Z3_API Z3_del_context(Z3_context c); #endif #ifdef Conly @@ -1573,7 +1602,7 @@ extern "C" { def_API('Z3_inc_ref', VOID, (_in(CONTEXT), _in(AST))) */ - void Z3_API Z3_inc_ref(__in Z3_context c, __in Z3_ast a); + void Z3_API Z3_inc_ref(Z3_context c, Z3_ast a); /** \brief Decrement the reference counter of the given AST. @@ -1582,7 +1611,7 @@ extern "C" { def_API('Z3_dec_ref', VOID, (_in(CONTEXT), _in(AST))) */ - void Z3_API Z3_dec_ref(__in Z3_context c, __in Z3_ast a); + void Z3_API Z3_dec_ref(Z3_context c, Z3_ast a); #endif /** @@ -1592,7 +1621,7 @@ extern "C" { def_API('Z3_update_param_value', VOID, (_in(CONTEXT), _in(STRING), _in(STRING))) */ - void Z3_API Z3_update_param_value(__in Z3_context c, __in Z3_string param_id, __in Z3_string param_value); + void Z3_API Z3_update_param_value(Z3_context c, Z3_string param_id, Z3_string param_value); #ifdef CorML4 /** @@ -1601,7 +1630,7 @@ extern "C" { def_API('Z3_interrupt', VOID, (_in(CONTEXT),)) */ - void Z3_API Z3_interrupt(__in Z3_context c); + void Z3_API Z3_interrupt(Z3_context c); #endif @@ -1623,7 +1652,7 @@ extern "C" { def_API('Z3_mk_params', PARAMS, (_in(CONTEXT),)) */ - Z3_params Z3_API Z3_mk_params(__in Z3_context c); + Z3_params Z3_API Z3_mk_params(Z3_context c); #ifdef Conly /** @@ -1631,14 +1660,14 @@ extern "C" { def_API('Z3_params_inc_ref', VOID, (_in(CONTEXT), _in(PARAMS))) */ - void Z3_API Z3_params_inc_ref(__in Z3_context c, __in Z3_params p); + void Z3_API Z3_params_inc_ref(Z3_context c, Z3_params p); /** \brief Decrement the reference counter of the given parameter set. def_API('Z3_params_dec_ref', VOID, (_in(CONTEXT), _in(PARAMS))) */ - void Z3_API Z3_params_dec_ref(__in Z3_context c, __in Z3_params p); + void Z3_API Z3_params_dec_ref(Z3_context c, Z3_params p); #endif /** @@ -1646,28 +1675,28 @@ extern "C" { def_API('Z3_params_set_bool', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(BOOL))) */ - void Z3_API Z3_params_set_bool(__in Z3_context c, __in Z3_params p, __in Z3_symbol k, __in Z3_bool v); + void Z3_API Z3_params_set_bool(Z3_context c, Z3_params p, Z3_symbol k, Z3_bool v); /** \brief Add a unsigned parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_uint', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(UINT))) */ - void Z3_API Z3_params_set_uint(__in Z3_context c, __in Z3_params p, __in Z3_symbol k, __in unsigned v); + void Z3_API Z3_params_set_uint(Z3_context c, Z3_params p, Z3_symbol k, unsigned v); /** \brief Add a double parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_double', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(DOUBLE))) */ - void Z3_API Z3_params_set_double(__in Z3_context c, __in Z3_params p, __in Z3_symbol k, __in double v); + void Z3_API Z3_params_set_double(Z3_context c, Z3_params p, Z3_symbol k, double v); /** \brief Add a symbol parameter \c k with value \c v to the parameter set \c p. def_API('Z3_params_set_symbol', VOID, (_in(CONTEXT), _in(PARAMS), _in(SYMBOL), _in(SYMBOL))) */ - void Z3_API Z3_params_set_symbol(__in Z3_context c, __in Z3_params p, __in Z3_symbol k, __in Z3_symbol v); + void Z3_API Z3_params_set_symbol(Z3_context c, Z3_params p, Z3_symbol k, Z3_symbol v); /** \brief Convert a parameter set into a string. This function is mainly used for printing the @@ -1675,7 +1704,7 @@ extern "C" { def_API('Z3_params_to_string', STRING, (_in(CONTEXT), _in(PARAMS))) */ - Z3_string Z3_API Z3_params_to_string(__in Z3_context c, __in Z3_params p); + Z3_string Z3_API Z3_params_to_string(Z3_context c, Z3_params p); /** \brief Validate the parameter set \c p against the parameter description set \c d. @@ -1684,7 +1713,7 @@ extern "C" { def_API('Z3_params_validate', VOID, (_in(CONTEXT), _in(PARAMS), _in(PARAM_DESCRS))) */ - void Z3_API Z3_params_validate(__in Z3_context c, __in Z3_params p, __in Z3_param_descrs d); + void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d); #endif @@ -1702,14 +1731,14 @@ extern "C" { def_API('Z3_param_descrs_inc_ref', VOID, (_in(CONTEXT), _in(PARAM_DESCRS))) */ - void Z3_API Z3_param_descrs_inc_ref(__in Z3_context c, __in Z3_param_descrs p); + void Z3_API Z3_param_descrs_inc_ref(Z3_context c, Z3_param_descrs p); /** \brief Decrement the reference counter of the given parameter description set. def_API('Z3_param_descrs_dec_ref', VOID, (_in(CONTEXT), _in(PARAM_DESCRS))) */ - void Z3_API Z3_param_descrs_dec_ref(__in Z3_context c, __in Z3_param_descrs p); + void Z3_API Z3_param_descrs_dec_ref(Z3_context c, Z3_param_descrs p); #endif /** @@ -1717,14 +1746,14 @@ extern "C" { def_API('Z3_param_descrs_get_kind', UINT, (_in(CONTEXT), _in(PARAM_DESCRS), _in(SYMBOL))) */ - Z3_param_kind Z3_API Z3_param_descrs_get_kind(__in Z3_context c, __in Z3_param_descrs p, __in Z3_symbol n); + Z3_param_kind Z3_API Z3_param_descrs_get_kind(Z3_context c, Z3_param_descrs p, Z3_symbol n); /** \brief Return the number of parameters in the given parameter description set. def_API('Z3_param_descrs_size', UINT, (_in(CONTEXT), _in(PARAM_DESCRS))) */ - unsigned Z3_API Z3_param_descrs_size(__in Z3_context c, __in Z3_param_descrs p); + unsigned Z3_API Z3_param_descrs_size(Z3_context c, Z3_param_descrs p); /** \brief Return the number of parameters in the given parameter description set. @@ -1733,7 +1762,7 @@ extern "C" { def_API('Z3_param_descrs_get_name', SYMBOL, (_in(CONTEXT), _in(PARAM_DESCRS), _in(UINT))) */ - Z3_symbol Z3_API Z3_param_descrs_get_name(__in Z3_context c, __in Z3_param_descrs p, __in unsigned i); + Z3_symbol Z3_API Z3_param_descrs_get_name(Z3_context c, Z3_param_descrs p, unsigned i); /** \brief Convert a parameter description set into a string. This function is mainly used for printing the @@ -1741,7 +1770,7 @@ extern "C" { def_API('Z3_param_descrs_to_string', STRING, (_in(CONTEXT), _in(PARAM_DESCRS))) */ - Z3_string Z3_API Z3_param_descrs_to_string(__in Z3_context c, __in Z3_param_descrs p); + Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p); /*@}*/ #endif @@ -1771,7 +1800,7 @@ extern "C" { def_API('Z3_mk_int_symbol', SYMBOL, (_in(CONTEXT), _in(INT))) */ - Z3_symbol Z3_API Z3_mk_int_symbol(__in Z3_context c, __in int i); + Z3_symbol Z3_API Z3_mk_int_symbol(Z3_context c, int i); /** \brief Create a Z3 symbol using a C string. @@ -1782,7 +1811,7 @@ extern "C" { def_API('Z3_mk_string_symbol', SYMBOL, (_in(CONTEXT), _in(STRING))) */ - Z3_symbol Z3_API Z3_mk_string_symbol(__in Z3_context c, __in Z3_string s); + Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, Z3_string s); /*@}*/ @@ -1806,7 +1835,7 @@ extern "C" { def_API('Z3_mk_uninterpreted_sort', SORT, (_in(CONTEXT), _in(SYMBOL))) */ - Z3_sort Z3_API Z3_mk_uninterpreted_sort(__in Z3_context c, __in Z3_symbol s); + Z3_sort Z3_API Z3_mk_uninterpreted_sort(Z3_context c, Z3_symbol s); /** \brief Create the Boolean type. @@ -1815,7 +1844,7 @@ extern "C" { def_API('Z3_mk_bool_sort', SORT, (_in(CONTEXT), )) */ - Z3_sort Z3_API Z3_mk_bool_sort(__in Z3_context c); + Z3_sort Z3_API Z3_mk_bool_sort(Z3_context c); /** \brief Create the integer type. @@ -1828,7 +1857,7 @@ extern "C" { def_API('Z3_mk_int_sort', SORT, (_in(CONTEXT), )) */ - Z3_sort Z3_API Z3_mk_int_sort(__in Z3_context c); + Z3_sort Z3_API Z3_mk_int_sort(Z3_context c); /** \brief Create the real type. @@ -1837,7 +1866,7 @@ extern "C" { def_API('Z3_mk_real_sort', SORT, (_in(CONTEXT), )) */ - Z3_sort Z3_API Z3_mk_real_sort(__in Z3_context c); + Z3_sort Z3_API Z3_mk_real_sort(Z3_context c); /** \brief Create a bit-vector type of the given size. @@ -1848,7 +1877,7 @@ extern "C" { def_API('Z3_mk_bv_sort', SORT, (_in(CONTEXT), _in(UINT))) */ - Z3_sort Z3_API Z3_mk_bv_sort(__in Z3_context c, __in unsigned sz); + Z3_sort Z3_API Z3_mk_bv_sort(Z3_context c, unsigned sz); /** \brief Create a named finite domain sort. @@ -1863,7 +1892,7 @@ extern "C" { def_API('Z3_mk_finite_domain_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT64))) */ - Z3_sort Z3_API Z3_mk_finite_domain_sort(__in Z3_context c, __in Z3_symbol name, __in unsigned __int64 size); + Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, unsigned __int64 size); /** \brief Create an array type. @@ -1876,7 +1905,7 @@ extern "C" { def_API('Z3_mk_array_sort', SORT, (_in(CONTEXT), _in(SORT), _in(SORT))) */ - Z3_sort Z3_API Z3_mk_array_sort(__in Z3_context c, __in Z3_sort domain, __in Z3_sort range); + Z3_sort Z3_API Z3_mk_array_sort(Z3_context c, Z3_sort domain, Z3_sort range); /** \brief Create a tuple type. @@ -1898,13 +1927,13 @@ extern "C" { def_API('Z3_mk_tuple_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _out(FUNC_DECL), _out_array(2, FUNC_DECL))) */ - Z3_sort Z3_API Z3_mk_tuple_sort(__in Z3_context c, - __in Z3_symbol mk_tuple_name, - __in unsigned num_fields, - __in_ecount(num_fields) Z3_symbol const field_names[], - __in_ecount(num_fields) Z3_sort const field_sorts[], - __out Z3_func_decl * mk_tuple_decl, - __out_ecount(num_fields) Z3_func_decl proj_decl[]); + Z3_sort Z3_API Z3_mk_tuple_sort(Z3_context c, + Z3_symbol mk_tuple_name, + unsigned num_fields, + Z3_symbol const field_names[], + Z3_sort const field_sorts[], + Z3_func_decl * mk_tuple_decl, + Z3_func_decl proj_decl[]); /** \brief Create a enumeration sort. @@ -1932,12 +1961,12 @@ extern "C" { def_API('Z3_mk_enumeration_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SYMBOL), _out_array(2, FUNC_DECL), _out_array(2, FUNC_DECL))) */ - Z3_sort Z3_API Z3_mk_enumeration_sort(__in Z3_context c, - __in Z3_symbol name, - __in unsigned n, - __in_ecount(n) Z3_symbol const enum_names[], - __out_ecount(n) Z3_func_decl enum_consts[], - __out_ecount(n) Z3_func_decl enum_testers[]); + Z3_sort Z3_API Z3_mk_enumeration_sort(Z3_context c, + Z3_symbol name, + unsigned n, + Z3_symbol const enum_names[], + Z3_func_decl enum_consts[], + Z3_func_decl enum_testers[]); /** \brief Create a list sort @@ -1960,15 +1989,15 @@ extern "C" { def_API('Z3_mk_list_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(SORT), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL), _out(FUNC_DECL))) */ - Z3_sort Z3_API Z3_mk_list_sort(__in Z3_context c, - __in Z3_symbol name, - __in Z3_sort elem_sort, - __out Z3_func_decl* nil_decl, - __out Z3_func_decl* is_nil_decl, - __out Z3_func_decl* cons_decl, - __out Z3_func_decl* is_cons_decl, - __out Z3_func_decl* head_decl, - __out Z3_func_decl* tail_decl + Z3_sort Z3_API Z3_mk_list_sort(Z3_context c, + Z3_symbol name, + Z3_sort elem_sort, + Z3_func_decl* nil_decl, + Z3_func_decl* is_nil_decl, + Z3_func_decl* cons_decl, + Z3_func_decl* is_cons_decl, + Z3_func_decl* head_decl, + Z3_func_decl* tail_decl ); BEGIN_MLAPI_EXCLUDE @@ -1989,13 +2018,13 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_mk_constructor', CONSTRUCTOR, (_in(CONTEXT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(3, SYMBOL), _in_array(3, SORT), _in_array(3, UINT))) */ - Z3_constructor Z3_API Z3_mk_constructor(__in Z3_context c, - __in Z3_symbol name, - __in Z3_symbol recognizer, - __in unsigned num_fields, - __in_ecount(num_fields) Z3_symbol const field_names[], - __in_ecount(num_fields) Z3_sort_opt const sorts[], - __in_ecount(num_fields) unsigned sort_refs[] + Z3_constructor Z3_API Z3_mk_constructor(Z3_context c, + Z3_symbol name, + Z3_symbol recognizer, + unsigned num_fields, + Z3_symbol const field_names[], + Z3_sort_opt const sorts[], + unsigned sort_refs[] ); /** @@ -2006,7 +2035,7 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_del_constructor', VOID, (_in(CONTEXT), _in(CONSTRUCTOR))) */ - void Z3_API Z3_del_constructor(__in Z3_context c, __in Z3_constructor constr); + void Z3_API Z3_del_constructor(Z3_context c, Z3_constructor constr); /** \brief Create datatype, such as lists, trees, records, enumerations or unions of records. @@ -2019,10 +2048,10 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_mk_datatype', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _inout_array(2, CONSTRUCTOR))) */ - Z3_sort Z3_API Z3_mk_datatype(__in Z3_context c, - __in Z3_symbol name, - __in unsigned num_constructors, - __inout_ecount(num_constructors) Z3_constructor constructors[]); + Z3_sort Z3_API Z3_mk_datatype(Z3_context c, + Z3_symbol name, + unsigned num_constructors, + Z3_constructor constructors[]); /** @@ -2034,9 +2063,9 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_mk_constructor_list', CONSTRUCTOR_LIST, (_in(CONTEXT), _in(UINT), _in_array(1, CONSTRUCTOR))) */ - Z3_constructor_list Z3_API Z3_mk_constructor_list(__in Z3_context c, - __in unsigned num_constructors, - __in_ecount(num_constructors) Z3_constructor const constructors[]); + Z3_constructor_list Z3_API Z3_mk_constructor_list(Z3_context c, + unsigned num_constructors, + Z3_constructor const constructors[]); /** \brief Reclaim memory allocated for constructor list. @@ -2048,7 +2077,7 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_del_constructor_list', VOID, (_in(CONTEXT), _in(CONSTRUCTOR_LIST))) */ - void Z3_API Z3_del_constructor_list(__in Z3_context c, __in Z3_constructor_list clist); + void Z3_API Z3_del_constructor_list(Z3_context c, Z3_constructor_list clist); /** \brief Create mutually recursive datatypes. @@ -2061,11 +2090,11 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_mk_datatypes', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, SYMBOL), _out_array(1, SORT), _inout_array(1, CONSTRUCTOR_LIST))) */ - void Z3_API Z3_mk_datatypes(__in Z3_context c, - __in unsigned num_sorts, - __in_ecount(num_sorts) Z3_symbol const sort_names[], - __out_ecount(num_sorts) Z3_sort sorts[], - __inout_ecount(num_sorts) Z3_constructor_list constructor_lists[]); + void Z3_API Z3_mk_datatypes(Z3_context c, + unsigned num_sorts, + Z3_symbol const sort_names[], + Z3_sort sorts[], + Z3_constructor_list constructor_lists[]); /** \brief Query constructor for declared functions. @@ -2079,12 +2108,12 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_query_constructor', VOID, (_in(CONTEXT), _in(CONSTRUCTOR), _in(UINT), _out(FUNC_DECL), _out(FUNC_DECL), _out_array(2, FUNC_DECL))) */ - void Z3_API Z3_query_constructor(__in Z3_context c, - __in Z3_constructor constr, - __in unsigned num_fields, - __out Z3_func_decl* constructor, - __out Z3_func_decl* tester, - __out_ecount(num_fields) Z3_func_decl accessors[]); + void Z3_API Z3_query_constructor(Z3_context c, + Z3_constructor constr, + unsigned num_fields, + Z3_func_decl* constructor, + Z3_func_decl* tester, + Z3_func_decl accessors[]); END_MLAPI_EXCLUDE /*@}*/ @@ -2114,9 +2143,9 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_func_decl', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT))) */ - Z3_func_decl Z3_API Z3_mk_func_decl(__in Z3_context c, __in Z3_symbol s, - __in unsigned domain_size, __in_ecount(domain_size) Z3_sort const domain[], - __in Z3_sort range); + Z3_func_decl Z3_API Z3_mk_func_decl(Z3_context c, Z3_symbol s, + unsigned domain_size, Z3_sort const domain[], + Z3_sort range); /** @@ -2127,10 +2156,10 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_app', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST))) */ Z3_ast Z3_API Z3_mk_app( - __in Z3_context c, - __in Z3_func_decl d, - __in unsigned num_args, - __in_ecount(num_args) Z3_ast const args[]); + Z3_context c, + Z3_func_decl d, + unsigned num_args, + Z3_ast const args[]); /** \brief Declare and create a constant. @@ -2148,7 +2177,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_const', AST, (_in(CONTEXT), _in(SYMBOL), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_const(__in Z3_context c, __in Z3_symbol s, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_const(Z3_context c, Z3_symbol s, Z3_sort ty); /** \brief Declare a fresh constant or function. @@ -2162,9 +2191,9 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_fresh_func_decl', FUNC_DECL, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SORT), _in(SORT))) */ - Z3_func_decl Z3_API Z3_mk_fresh_func_decl(__in Z3_context c, __in Z3_string prefix, - __in unsigned domain_size, __in_ecount(domain_size) Z3_sort const domain[], - __in Z3_sort range); + Z3_func_decl Z3_API Z3_mk_fresh_func_decl(Z3_context c, Z3_string prefix, + unsigned domain_size, Z3_sort const domain[], + Z3_sort range); /** \brief Declare and create a fresh constant. @@ -2181,7 +2210,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_fresh_const', AST, (_in(CONTEXT), _in(STRING), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_fresh_const(__in Z3_context c, __in Z3_string prefix, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_fresh_const(Z3_context c, Z3_string prefix, Z3_sort ty); /*@}*/ /** @@ -2193,14 +2222,14 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_true', AST, (_in(CONTEXT), )) */ - Z3_ast Z3_API Z3_mk_true(__in Z3_context c); + Z3_ast Z3_API Z3_mk_true(Z3_context c); /** \brief Create an AST node representing \c false. def_API('Z3_mk_false', AST, (_in(CONTEXT), )) */ - Z3_ast Z3_API Z3_mk_false(__in Z3_context c); + Z3_ast Z3_API Z3_mk_false(Z3_context c); /** \brief \mlh mk_eq c l r \endmlh @@ -2210,7 +2239,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_eq', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_eq(__in Z3_context c, __in Z3_ast l, __in Z3_ast r); + Z3_ast Z3_API Z3_mk_eq(Z3_context c, Z3_ast l, Z3_ast r); /** \conly \brief Create an AST node representing distinct(args[0], ..., args[num_args-1]). @@ -2227,7 +2256,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_distinct', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ - Z3_ast Z3_API Z3_mk_distinct(__in Z3_context c, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_mk_distinct(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief \mlh mk_not c a \endmlh @@ -2237,7 +2266,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_not', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_not(__in Z3_context c, __in Z3_ast a); + Z3_ast Z3_API Z3_mk_not(Z3_context c, Z3_ast a); /** \brief \mlh mk_ite c t1 t2 t2 \endmlh @@ -2249,7 +2278,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_ite', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_ite(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2, __in Z3_ast t3); + Z3_ast Z3_API Z3_mk_ite(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3); /** \brief \mlh mk_iff c t1 t2 \endmlh @@ -2259,7 +2288,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_iff', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_iff(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_iff(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_implies c t1 t2 \endmlh @@ -2269,7 +2298,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_implies', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_implies(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_implies(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_xor c t1 t2 \endmlh @@ -2279,7 +2308,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_xor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_xor(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_xor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \conly \brief Create an AST node representing args[0] and ... and args[num_args-1]. @@ -2292,7 +2321,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_and', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ - Z3_ast Z3_API Z3_mk_and(__in Z3_context c, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_mk_and(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \conly \brief Create an AST node representing args[0] or ... or args[num_args-1]. @@ -2305,7 +2334,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_or', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ - Z3_ast Z3_API Z3_mk_or(__in Z3_context c, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_mk_or(Z3_context c, unsigned num_args, Z3_ast const args[]); /*@}*/ /** @@ -2323,7 +2352,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_add', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ - Z3_ast Z3_API Z3_mk_add(__in Z3_context c, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_mk_add(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \conly \brief Create an AST node representing args[0] * ... * args[num_args-1]. @@ -2337,7 +2366,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_mul', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ - Z3_ast Z3_API Z3_mk_mul(__in Z3_context c, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_mk_mul(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \conly \brief Create an AST node representing args[0] - ... - args[num_args - 1]. @@ -2350,7 +2379,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_sub', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ - Z3_ast Z3_API Z3_mk_sub(__in Z3_context c, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_mk_sub(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \conly \brief Create an AST node representing -arg. @@ -2360,7 +2389,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_unary_minus', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_unary_minus(__in Z3_context c, __in Z3_ast arg); + Z3_ast Z3_API Z3_mk_unary_minus(Z3_context c, Z3_ast arg); /** \conly \brief Create an AST node representing arg1 div arg2. @@ -2372,7 +2401,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_div', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_div(__in Z3_context c, __in Z3_ast arg1, __in Z3_ast arg2); + Z3_ast Z3_API Z3_mk_div(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \conly \brief Create an AST node representing arg1 mod arg2. @@ -2382,7 +2411,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_mod', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_mod(__in Z3_context c, __in Z3_ast arg1, __in Z3_ast arg2); + Z3_ast Z3_API Z3_mk_mod(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \conly \brief Create an AST node representing arg1 rem arg2. @@ -2392,7 +2421,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_rem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_rem(__in Z3_context c, __in Z3_ast arg1, __in Z3_ast arg2); + Z3_ast Z3_API Z3_mk_rem(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \conly \brief Create an AST node representing arg1^arg2. @@ -2401,7 +2430,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_power', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_power(__in Z3_context c, __in Z3_ast arg1, __in Z3_ast arg2); + Z3_ast Z3_API Z3_mk_power(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief \mlh mk_lt c t1 t2 \endmlh @@ -2411,7 +2440,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_lt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_lt(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_lt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_le c t1 t2 \endmlh @@ -2421,7 +2450,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_le', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_le(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_le(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_gt c t1 t2 \endmlh @@ -2431,7 +2460,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_gt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_gt(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_gt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_ge c t1 t2 \endmlh @@ -2441,7 +2470,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_ge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_ge(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_ge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_int2real c t1 \endmlh @@ -2461,7 +2490,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_int2real', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_int2real(__in Z3_context c, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_int2real(Z3_context c, Z3_ast t1); /** \brief \mlh mk_real2int c t1 \endmlh @@ -2475,7 +2504,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_real2int', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_real2int(__in Z3_context c, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_real2int(Z3_context c, Z3_ast t1); /** \brief \mlh mk_is_int c t1 \endmlh @@ -2486,7 +2515,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_is_int', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_is_int(__in Z3_context c, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_is_int(Z3_context c, Z3_ast t1); /*@}*/ /** @@ -2501,7 +2530,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvnot', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvnot(__in Z3_context c, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_bvnot(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvredand c t1 \endmlh @@ -2511,7 +2540,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvredand', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvredand(__in Z3_context c, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_bvredand(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvredor c t1 \endmlh @@ -2521,7 +2550,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvredor', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvredor(__in Z3_context c, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_bvredor(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvand c t1 t2 \endmlh @@ -2531,7 +2560,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvand', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvand(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvand(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvor c t1 t2 \endmlh @@ -2541,7 +2570,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvor(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvxor c t1 t2 \endmlh @@ -2551,7 +2580,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvxor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvxor(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvxor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvnand c t1 t2 \endmlh @@ -2561,7 +2590,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvnand', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvnand(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvnand(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvnor c t1 t2 \endmlh @@ -2571,7 +2600,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvnor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvnor(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvnor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvxnor c t1 t2 \endmlh @@ -2581,7 +2610,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvxnor', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvxnor(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvxnor(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvneg c t1 \endmlh @@ -2591,7 +2620,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvneg', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvneg(__in Z3_context c, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_bvneg(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvadd c t1 t2 \endmlh @@ -2601,7 +2630,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvadd', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvadd(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvadd(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsub c t1 t2 \endmlh @@ -2611,7 +2640,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsub', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsub(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsub(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvmul c t1 t2 \endmlh @@ -2621,7 +2650,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvmul', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvmul(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvmul(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvudiv c t1 t2 \endmlh @@ -2635,7 +2664,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvudiv', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvudiv(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvudiv(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsdiv c t1 t2 \endmlh @@ -2653,7 +2682,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsdiv', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsdiv(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsdiv(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvurem c t1 t2 \endmlh @@ -2667,7 +2696,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvurem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvurem(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvurem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsrem c t1 t2 \endmlh @@ -2684,7 +2713,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsrem', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsrem(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsrem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsmod c t1 t2 \endmlh @@ -2698,7 +2727,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsmod', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsmod(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsmod(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvult c t1 t2 \endmlh @@ -2708,7 +2737,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvult', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvult(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvult(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvslt c t1 t2 \endmlh @@ -2726,7 +2755,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvslt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvslt(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvslt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvule c t1 t2 \endmlh @@ -2736,7 +2765,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvule', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvule(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvule(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsle c t1 t2 \endmlh @@ -2746,7 +2775,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsle', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsle(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsle(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvuge c t1 t2 \endmlh @@ -2756,7 +2785,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvuge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvuge(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvuge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsge c t1 t2 \endmlh @@ -2766,7 +2795,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsge', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsge(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsge(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvugt c t1 t2 \endmlh @@ -2776,7 +2805,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvugt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvugt(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvugt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsgt c t1 t2 \endmlh @@ -2786,7 +2815,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsgt', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsgt(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsgt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_concat c t1 t2 \endmlh @@ -2799,7 +2828,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_concat', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_concat(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_concat(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_extract c high low t1 \endmlh @@ -2811,7 +2840,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_extract', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_extract(__in Z3_context c, __in unsigned high, __in unsigned low, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_extract(Z3_context c, unsigned high, unsigned low, Z3_ast t1); /** \brief \mlh mk_sign_ext c i t1 \endmlh @@ -2823,7 +2852,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_sign_ext', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_sign_ext(__in Z3_context c, __in unsigned i, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_sign_ext(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_zero_ext c i t1 \endmlh @@ -2835,7 +2864,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_zero_ext', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_zero_ext(__in Z3_context c, __in unsigned i, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_zero_ext(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_repeat c i t1 \endmlh @@ -2845,7 +2874,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_repeat', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_repeat(__in Z3_context c, __in unsigned i, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_repeat(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_bvshl c t1 t2 \endmlh @@ -2862,7 +2891,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvshl', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvshl(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvshl(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvlshr c t1 t2 \endmlh @@ -2879,7 +2908,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvlshr', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvlshr(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvlshr(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvashr c t1 t2 \endmlh @@ -2897,7 +2926,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvashr', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvashr(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvashr(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_rotate_left c i t1 \endmlh @@ -2907,7 +2936,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_rotate_left', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_rotate_left(__in Z3_context c, __in unsigned i, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_rotate_left(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_rotate_right c i t1 \endmlh @@ -2917,7 +2946,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_rotate_right', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_rotate_right(__in Z3_context c, __in unsigned i, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_rotate_right(Z3_context c, unsigned i, Z3_ast t1); /** \brief \mlh mk_ext_rotate_left c t1 t2 \endmlh @@ -2927,7 +2956,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_ext_rotate_left', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_ext_rotate_left(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_ext_rotate_left(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_ext_rotate_right c t1 t2 \endmlh @@ -2937,7 +2966,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_ext_rotate_right', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_ext_rotate_right(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_ext_rotate_right(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_int2bv c n t1 \endmlh @@ -2951,7 +2980,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_int2bv', AST, (_in(CONTEXT), _in(UINT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_int2bv(__in Z3_context c, __in unsigned n, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_int2bv(Z3_context c, unsigned n, Z3_ast t1); /** \brief \mlh mk_bv2int c t1 is_signed \endmlh @@ -2969,7 +2998,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bv2int', AST, (_in(CONTEXT), _in(AST), _in(BOOL))) */ - Z3_ast Z3_API Z3_mk_bv2int(__in Z3_context c,__in Z3_ast t1, Z3_bool is_signed); + Z3_ast Z3_API Z3_mk_bv2int(Z3_context c,Z3_ast t1, Z3_bool is_signed); /** \brief \mlh mk_bvadd_no_overflow c t1 t2 is_signed \endmlh @@ -2980,7 +3009,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvadd_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ - Z3_ast Z3_API Z3_mk_bvadd_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2, Z3_bool is_signed); + Z3_ast Z3_API Z3_mk_bvadd_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed); /** \brief \mlh mk_bvadd_no_underflow c t1 t2 \endmlh @@ -2991,7 +3020,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvadd_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvadd_no_underflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvadd_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsub_no_overflow c t1 t2 \endmlh @@ -3002,7 +3031,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsub_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsub_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsub_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvsub_no_underflow c t1 t2 is_signed \endmlh @@ -3013,7 +3042,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsub_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ - Z3_ast Z3_API Z3_mk_bvsub_no_underflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2, Z3_bool is_signed); + Z3_ast Z3_API Z3_mk_bvsub_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed); /** \brief \mlh mk_bvsdiv_no_overflow c t1 t2 \endmlh @@ -3024,7 +3053,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvsdiv_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvsdiv_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief \mlh mk_bvneg_no_overflow c t1 \endmlh @@ -3035,7 +3064,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvneg_no_overflow', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvneg_no_overflow(__in Z3_context c, __in Z3_ast t1); + Z3_ast Z3_API Z3_mk_bvneg_no_overflow(Z3_context c, Z3_ast t1); /** \brief \mlh mk_bvmul_no_overflow c t1 t2 is_signed \endmlh @@ -3046,7 +3075,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvmul_no_overflow', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(BOOL))) */ - Z3_ast Z3_API Z3_mk_bvmul_no_overflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2, Z3_bool is_signed); + Z3_ast Z3_API Z3_mk_bvmul_no_overflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed); /** \brief \mlh mk_bvmul_no_underflow c t1 t2 \endmlh @@ -3057,7 +3086,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bvmul_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_bvmul_no_underflow(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2); /*@}*/ /** @@ -3079,7 +3108,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_select', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_select(__in Z3_context c, __in Z3_ast a, __in Z3_ast i); + Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i); /** \brief \mlh mk_store c a i v \endmlh @@ -3098,7 +3127,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_store', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_store(__in Z3_context c, __in Z3_ast a, __in Z3_ast i, __in Z3_ast v); + Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v); /** \brief Create the constant array. @@ -3112,7 +3141,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_const_array', AST, (_in(CONTEXT), _in(SORT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_const_array(__in Z3_context c, __in Z3_sort domain, __in Z3_ast v); + Z3_ast Z3_API Z3_mk_const_array(Z3_context c, Z3_sort domain, Z3_ast v); /** \brief \mlh mk_map f n args \endmlh @@ -3128,7 +3157,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_map', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST))) */ - Z3_ast Z3_API Z3_mk_map(__in Z3_context c, __in Z3_func_decl f, unsigned n, __in Z3_ast const* args); + Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args); /** \brief Access the array default value. @@ -3140,7 +3169,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_array_default', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_array_default(__in Z3_context c, __in Z3_ast array); + Z3_ast Z3_API Z3_mk_array_default(Z3_context c, Z3_ast array); /*@}*/ /** @@ -3152,21 +3181,21 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_set_sort', SORT, (_in(CONTEXT), _in(SORT))) */ - Z3_sort Z3_API Z3_mk_set_sort(__in Z3_context c, __in Z3_sort ty); + Z3_sort Z3_API Z3_mk_set_sort(Z3_context c, Z3_sort ty); /** \brief Create the empty set. def_API('Z3_mk_empty_set', AST, (_in(CONTEXT), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_empty_set(__in Z3_context c, __in Z3_sort domain); + Z3_ast Z3_API Z3_mk_empty_set(Z3_context c, Z3_sort domain); /** \brief Create the full set. def_API('Z3_mk_full_set', AST, (_in(CONTEXT), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_full_set(__in Z3_context c, __in Z3_sort domain); + Z3_ast Z3_API Z3_mk_full_set(Z3_context c, Z3_sort domain); /** \brief Add an element to a set. @@ -3175,7 +3204,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_set_add', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_set_add(__in Z3_context c, __in Z3_ast set, __in Z3_ast elem); + Z3_ast Z3_API Z3_mk_set_add(Z3_context c, Z3_ast set, Z3_ast elem); /** \brief Remove an element to a set. @@ -3184,35 +3213,35 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_set_del', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_set_del(__in Z3_context c, __in Z3_ast set, __in Z3_ast elem); + Z3_ast Z3_API Z3_mk_set_del(Z3_context c, Z3_ast set, Z3_ast elem); /** \brief Take the union of a list of sets. def_API('Z3_mk_set_union', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ - Z3_ast Z3_API Z3_mk_set_union(__in Z3_context c, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_mk_set_union(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Take the intersection of a list of sets. def_API('Z3_mk_set_intersect', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ - Z3_ast Z3_API Z3_mk_set_intersect(__in Z3_context c, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_mk_set_intersect(Z3_context c, unsigned num_args, Z3_ast const args[]); /** \brief Take the set difference between two sets. def_API('Z3_mk_set_difference', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_set_difference(__in Z3_context c, __in Z3_ast arg1, __in Z3_ast arg2); + Z3_ast Z3_API Z3_mk_set_difference(Z3_context c, Z3_ast arg1, Z3_ast arg2); /** \brief Take the complement of a set. def_API('Z3_mk_set_complement', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_set_complement(__in Z3_context c, __in Z3_ast arg); + Z3_ast Z3_API Z3_mk_set_complement(Z3_context c, Z3_ast arg); /** \brief Check for set membership. @@ -3221,14 +3250,14 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_set_member', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_set_member(__in Z3_context c, __in Z3_ast elem, __in Z3_ast set); + Z3_ast Z3_API Z3_mk_set_member(Z3_context c, Z3_ast elem, Z3_ast set); /** \brief Check for subsetness of sets. def_API('Z3_mk_set_subset', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_set_subset(__in Z3_context c, __in Z3_ast arg1, __in Z3_ast arg2); + Z3_ast Z3_API Z3_mk_set_subset(Z3_context c, Z3_ast arg1, Z3_ast arg2); /*@}*/ /** @@ -3256,7 +3285,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_numeral', AST, (_in(CONTEXT), _in(STRING), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_numeral(__in Z3_context c, __in Z3_string numeral, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_numeral(Z3_context c, Z3_string numeral, Z3_sort ty); /** \brief Create a real from a fraction. @@ -3273,7 +3302,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_real', AST, (_in(CONTEXT), _in(INT), _in(INT))) */ - Z3_ast Z3_API Z3_mk_real(__in Z3_context c, __in int num, __in int den); + Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den); /** \brief Create a numeral of an int, bit-vector, or finite-domain sort. @@ -3285,7 +3314,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_int', AST, (_in(CONTEXT), _in(INT), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_int(__in Z3_context c, __in int v, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_int(Z3_context c, int v, Z3_sort ty); #ifdef Conly /** @@ -3298,7 +3327,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_unsigned_int', AST, (_in(CONTEXT), _in(UINT), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_unsigned_int(__in Z3_context c, __in unsigned v, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_unsigned_int(Z3_context c, unsigned v, Z3_sort ty); #endif /** @@ -3311,7 +3340,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_int64', AST, (_in(CONTEXT), _in(INT64), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_int64(__in Z3_context c, __in __int64 v, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_int64(Z3_context c, __int64 v, Z3_sort ty); #ifdef Conly /** @@ -3324,7 +3353,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_unsigned_int64', AST, (_in(CONTEXT), _in(UINT64), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_unsigned_int64(__in Z3_context c, __in unsigned __int64 v, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, unsigned __int64 v, Z3_sort ty); #endif /*@}*/ @@ -3355,8 +3384,8 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_pattern', PATTERN, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_pattern Z3_API Z3_mk_pattern( - __in Z3_context c, - __in unsigned num_patterns, __in_ecount(num_patterns) Z3_ast const terms[]); + Z3_context c, + unsigned num_patterns, Z3_ast const terms[]); /** \brief Create a bound variable. @@ -3387,7 +3416,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_bound', AST, (_in(CONTEXT), _in(UINT), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_bound(__in Z3_context c, __in unsigned index, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_bound(Z3_context c, unsigned index, Z3_sort ty); /** \brief Create a forall formula. It takes an expression \c body that contains bound variables @@ -3422,11 +3451,11 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_forall', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, PATTERN), _in(UINT), _in_array(4, SORT), _in_array(4, SYMBOL), _in(AST))) */ - Z3_ast Z3_API Z3_mk_forall(__in Z3_context c, __in unsigned weight, - __in unsigned num_patterns, __in_ecount(num_patterns) Z3_pattern const patterns[], - __in unsigned num_decls, __in_ecount(num_decls) Z3_sort const sorts[], - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in Z3_ast body); + Z3_ast Z3_API Z3_mk_forall(Z3_context c, unsigned weight, + unsigned num_patterns, Z3_pattern const patterns[], + unsigned num_decls, Z3_sort const sorts[], + Z3_symbol const decl_names[], + Z3_ast body); /** \brief Create an exists formula. Similar to #Z3_mk_forall. @@ -3438,11 +3467,11 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_exists', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, PATTERN), _in(UINT), _in_array(4, SORT), _in_array(4, SYMBOL), _in(AST))) */ - Z3_ast Z3_API Z3_mk_exists(__in Z3_context c, __in unsigned weight, - __in unsigned num_patterns, __in_ecount(num_patterns) Z3_pattern const patterns[], - __in unsigned num_decls, __in_ecount(num_decls) Z3_sort const sorts[], - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in Z3_ast body); + Z3_ast Z3_API Z3_mk_exists(Z3_context c, unsigned weight, + unsigned num_patterns, Z3_pattern const patterns[], + unsigned num_decls, Z3_sort const sorts[], + Z3_symbol const decl_names[], + Z3_ast body); /** \brief Create a quantifier - universal or existential, with pattern hints. @@ -3466,13 +3495,13 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_quantifier', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(UINT), _in_array(3, PATTERN), _in(UINT), _in_array(5, SORT), _in_array(5, SYMBOL), _in(AST))) */ Z3_ast Z3_API Z3_mk_quantifier( - __in Z3_context c, - __in Z3_bool is_forall, - __in unsigned weight, - __in unsigned num_patterns, __in_ecount(num_patterns) Z3_pattern const patterns[], - __in unsigned num_decls, __in_ecount(num_decls) Z3_sort const sorts[], - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in Z3_ast body); + Z3_context c, + Z3_bool is_forall, + unsigned weight, + unsigned num_patterns, Z3_pattern const patterns[], + unsigned num_decls, Z3_sort const sorts[], + Z3_symbol const decl_names[], + Z3_ast body); /** @@ -3500,16 +3529,16 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_quantifier_ex', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(5, PATTERN), _in(UINT), _in_array(7, AST), _in(UINT), _in_array(9, SORT), _in_array(9, SYMBOL), _in(AST))) */ Z3_ast Z3_API Z3_mk_quantifier_ex( - __in Z3_context c, - __in Z3_bool is_forall, - __in unsigned weight, - __in Z3_symbol quantifier_id, - __in Z3_symbol skolem_id, - __in unsigned num_patterns, __in_ecount(num_patterns) Z3_pattern const patterns[], - __in unsigned num_no_patterns, __in_ecount(num_no_patterns) Z3_ast const no_patterns[], - __in unsigned num_decls, __in_ecount(num_decls) Z3_sort const sorts[], - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in Z3_ast body); + Z3_context c, + Z3_bool is_forall, + unsigned weight, + Z3_symbol quantifier_id, + Z3_symbol skolem_id, + unsigned num_patterns, Z3_pattern const patterns[], + unsigned num_no_patterns, Z3_ast const no_patterns[], + unsigned num_decls, Z3_sort const sorts[], + Z3_symbol const decl_names[], + Z3_ast body); /** \brief Create a universal quantifier using a list of constants that @@ -3530,13 +3559,13 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_forall_const', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, APP), _in(UINT), _in_array(4, PATTERN), _in(AST))) */ Z3_ast Z3_API Z3_mk_forall_const( - __in Z3_context c, + Z3_context c, unsigned weight, unsigned num_bound, - __in_ecount(num_bound) Z3_app const bound[], + Z3_app const bound[], unsigned num_patterns, - __in_ecount(num_patterns) Z3_pattern const patterns[], - __in Z3_ast body + Z3_pattern const patterns[], + Z3_ast body ); /** @@ -3560,13 +3589,13 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_exists_const', AST, (_in(CONTEXT), _in(UINT), _in(UINT), _in_array(2, APP), _in(UINT), _in_array(4, PATTERN), _in(AST))) */ Z3_ast Z3_API Z3_mk_exists_const( - __in Z3_context c, + Z3_context c, unsigned weight, unsigned num_bound, - __in_ecount(num_bound) Z3_app const bound[], + Z3_app const bound[], unsigned num_patterns, - __in_ecount(num_patterns) Z3_pattern const patterns[], - __in Z3_ast body + Z3_pattern const patterns[], + Z3_ast body ); /** @@ -3577,12 +3606,12 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_quantifier_const', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(UINT), _in_array(3, APP), _in(UINT), _in_array(5, PATTERN), _in(AST))) */ Z3_ast Z3_API Z3_mk_quantifier_const( - __in Z3_context c, + Z3_context c, Z3_bool is_forall, unsigned weight, - unsigned num_bound, __in_ecount(num_bound) Z3_app const bound[], - unsigned num_patterns, __in_ecount(num_patterns) Z3_pattern const patterns[], - __in Z3_ast body + unsigned num_bound, Z3_app const bound[], + unsigned num_patterns, Z3_pattern const patterns[], + Z3_ast body ); @@ -3595,15 +3624,15 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_quantifier_const_ex', AST, (_in(CONTEXT), _in(BOOL), _in(UINT), _in(SYMBOL), _in(SYMBOL), _in(UINT), _in_array(5, APP), _in(UINT), _in_array(7, PATTERN), _in(UINT), _in_array(9, AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_quantifier_const_ex( - __in Z3_context c, + Z3_context c, Z3_bool is_forall, unsigned weight, - __in Z3_symbol quantifier_id, - __in Z3_symbol skolem_id, - unsigned num_bound, __in_ecount(num_bound) Z3_app const bound[], - unsigned num_patterns, __in_ecount(num_patterns) Z3_pattern const patterns[], - unsigned num_no_patterns, __in_ecount(num_no_patterns) Z3_ast const no_patterns[], - __in Z3_ast body + Z3_symbol quantifier_id, + Z3_symbol skolem_id, + unsigned num_bound, Z3_app const bound[], + unsigned num_patterns, Z3_pattern const patterns[], + unsigned num_no_patterns, Z3_ast const no_patterns[], + Z3_ast body ); /*@}*/ @@ -3632,7 +3661,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_symbol_kind', UINT, (_in(CONTEXT), _in(SYMBOL))) */ - Z3_symbol_kind Z3_API Z3_get_symbol_kind(__in Z3_context c, __in Z3_symbol s); + Z3_symbol_kind Z3_API Z3_get_symbol_kind(Z3_context c, Z3_symbol s); /** \brief \mlh get_symbol_int c s \endmlh @@ -3644,7 +3673,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_symbol_int', INT, (_in(CONTEXT), _in(SYMBOL))) */ - int Z3_API Z3_get_symbol_int(__in Z3_context c, __in Z3_symbol s); + int Z3_API Z3_get_symbol_int(Z3_context c, Z3_symbol s); /** \brief \mlh get_symbol_string c s \endmlh @@ -3660,7 +3689,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_symbol_string', STRING, (_in(CONTEXT), _in(SYMBOL))) */ - Z3_string Z3_API Z3_get_symbol_string(__in Z3_context c, __in Z3_symbol s); + Z3_string Z3_API Z3_get_symbol_string(Z3_context c, Z3_symbol s); /** @@ -3676,7 +3705,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_sort_name', SYMBOL, (_in(CONTEXT), _in(SORT))) */ - Z3_symbol Z3_API Z3_get_sort_name(__in Z3_context c, __in Z3_sort d); + Z3_symbol Z3_API Z3_get_sort_name(Z3_context c, Z3_sort d); /** \brief Return a unique identifier for \c s. @@ -3684,7 +3713,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_sort_id', UINT, (_in(CONTEXT), _in(SORT))) */ - unsigned Z3_API Z3_get_sort_id(__in Z3_context c, Z3_sort s); + unsigned Z3_API Z3_get_sort_id(Z3_context c, Z3_sort s); /** \mlonly {4 {L Redundant low-level API}} \endmlonly @@ -3696,7 +3725,7 @@ END_MLAPI_EXCLUDE def_API('Z3_sort_to_ast', AST, (_in(CONTEXT), _in(SORT))) */ - Z3_ast Z3_API Z3_sort_to_ast(__in Z3_context c, __in Z3_sort s); + Z3_ast Z3_API Z3_sort_to_ast(Z3_context c, Z3_sort s); /** \brief compare sorts. @@ -3704,7 +3733,7 @@ END_MLAPI_EXCLUDE def_API('Z3_is_eq_sort', BOOL, (_in(CONTEXT), _in(SORT), _in(SORT))) */ - Z3_bool Z3_API Z3_is_eq_sort(__in Z3_context c, __in Z3_sort s1, __in Z3_sort s2); + Z3_bool Z3_API Z3_is_eq_sort(Z3_context c, Z3_sort s1, Z3_sort s2); /** \brief Return the sort kind (e.g., array, tuple, int, bool, etc). @@ -3713,7 +3742,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_sort_kind', UINT, (_in(CONTEXT), _in(SORT))) */ - Z3_sort_kind Z3_API Z3_get_sort_kind(__in Z3_context c, __in Z3_sort t); + Z3_sort_kind Z3_API Z3_get_sort_kind(Z3_context c, Z3_sort t); /** @@ -3727,7 +3756,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_bv_sort_size', UINT, (_in(CONTEXT), _in(SORT))) */ - unsigned Z3_API Z3_get_bv_sort_size(__in Z3_context c, __in Z3_sort t); + unsigned Z3_API Z3_get_bv_sort_size(Z3_context c, Z3_sort t); /** \conly \brief Store the size of the sort in \c r. Return Z3_FALSE if the call failed. @@ -3736,7 +3765,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_finite_domain_sort_size', BOOL, (_in(CONTEXT), _in(SORT), _out(UINT64))) */ - Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(__in Z3_context c, __in Z3_sort s, __out_opt unsigned __int64* r); + Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, unsigned __int64* r); /** @@ -3750,7 +3779,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_array_sort_domain', SORT, (_in(CONTEXT), _in(SORT))) */ - Z3_sort Z3_API Z3_get_array_sort_domain(__in Z3_context c, __in Z3_sort t); + Z3_sort Z3_API Z3_get_array_sort_domain(Z3_context c, Z3_sort t); /** \brief \mlh get_array_sort_range c t \endmlh @@ -3763,7 +3792,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_array_sort_range', SORT, (_in(CONTEXT), _in(SORT))) */ - Z3_sort Z3_API Z3_get_array_sort_range(__in Z3_context c, __in Z3_sort t); + Z3_sort Z3_API Z3_get_array_sort_range(Z3_context c, Z3_sort t); /** @@ -3778,7 +3807,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_tuple_sort_mk_decl', FUNC_DECL, (_in(CONTEXT), _in(SORT))) */ - Z3_func_decl Z3_API Z3_get_tuple_sort_mk_decl(__in Z3_context c, __in Z3_sort t); + Z3_func_decl Z3_API Z3_get_tuple_sort_mk_decl(Z3_context c, Z3_sort t); /** \brief \mlh get_tuple_sort_num_fields c t \endmlh @@ -3791,7 +3820,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_tuple_sort_num_fields', UINT, (_in(CONTEXT), _in(SORT))) */ - unsigned Z3_API Z3_get_tuple_sort_num_fields(__in Z3_context c, __in Z3_sort t); + unsigned Z3_API Z3_get_tuple_sort_num_fields(Z3_context c, Z3_sort t); /** \brief \mlh get_tuple_sort_field_decl c t i \endmlh @@ -3806,7 +3835,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_tuple_sort_field_decl', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ - Z3_func_decl Z3_API Z3_get_tuple_sort_field_decl(__in Z3_context c, __in Z3_sort t, __in unsigned i); + Z3_func_decl Z3_API Z3_get_tuple_sort_field_decl(Z3_context c, Z3_sort t, unsigned i); /** \brief Return number of constructors for datatype. @@ -3820,7 +3849,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_datatype_sort_num_constructors', UINT, (_in(CONTEXT), _in(SORT))) */ unsigned Z3_API Z3_get_datatype_sort_num_constructors( - __in Z3_context c, __in Z3_sort t); + Z3_context c, Z3_sort t); /** \brief Return idx'th constructor. @@ -3835,7 +3864,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_datatype_sort_constructor', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_constructor( - __in Z3_context c, __in Z3_sort t, unsigned idx); + Z3_context c, Z3_sort t, unsigned idx); /** \brief Return idx'th recognizer. @@ -3850,7 +3879,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_datatype_sort_recognizer', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_recognizer( - __in Z3_context c, __in Z3_sort t, unsigned idx); + Z3_context c, Z3_sort t, unsigned idx); /** \brief Return idx_a'th accessor for the idx_c'th constructor. @@ -3866,8 +3895,30 @@ END_MLAPI_EXCLUDE def_API('Z3_get_datatype_sort_constructor_accessor', FUNC_DECL, (_in(CONTEXT), _in(SORT), _in(UINT), _in(UINT))) */ Z3_func_decl Z3_API Z3_get_datatype_sort_constructor_accessor( - __in Z3_context c, __in Z3_sort t, unsigned idx_c, unsigned idx_a); + Z3_context c, Z3_sort t, unsigned idx_c, unsigned idx_a); + /** + \brief Update record field with a value. + + This corresponds to the 'with' construct in OCaml. + It has the effect of updating a record field with a given value. + The remaining fields are left unchanged. It is the record + equivalent of an array store (see \sa Z3_mk_store). + If the datatype has more than one constructor, then the update function + behaves as identity if there is a miss-match between the accessor and + constructor. For example ((_ update-field car) nil 1) is nil, + while ((_ update-field car) (cons 2 nil) 1) is (cons 1 nil). + + + \pre Z3_get_sort_kind(Z3_get_sort(c, t)) == Z3_get_domain(c, field_access, 1) == Z3_DATATYPE_SORT + \pre Z3_get_sort(c, value) == Z3_get_range(c, field_access) + + + def_API('Z3_datatype_update_field', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_datatype_update_field( + Z3_context c, Z3_func_decl field_access, + Z3_ast t, Z3_ast value); /** \brief Return arity of relation. @@ -3878,7 +3929,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_relation_arity', UINT, (_in(CONTEXT), _in(SORT))) */ - unsigned Z3_API Z3_get_relation_arity(__in Z3_context c, __in Z3_sort s); + unsigned Z3_API Z3_get_relation_arity(Z3_context c, Z3_sort s); /** \brief Return sort at i'th column of relation sort. @@ -3890,9 +3941,32 @@ END_MLAPI_EXCLUDE def_API('Z3_get_relation_column', SORT, (_in(CONTEXT), _in(SORT), _in(UINT))) */ - Z3_sort Z3_API Z3_get_relation_column(__in Z3_context c, __in Z3_sort s, unsigned col); + Z3_sort Z3_API Z3_get_relation_column(Z3_context c, Z3_sort s, unsigned col); + /** + \brief Pseudo-Boolean relations. + + Encode p1 + p2 + ... + pn <= k + + def_API('Z3_mk_atmost', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in(UINT))) + */ + + Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args, + Z3_ast const args[], unsigned k); + + /** + \brief Pseudo-Boolean relations. + + Encode k1*p1 + k2*p2 + ... + kn*pn <= k + + def_API('Z3_mk_pble', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) + */ + + Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, + Z3_ast const args[], int coeffs[], + int k); + /** \mlonly {3 {L Function Declarations}} \endmlonly */ @@ -3903,7 +3977,7 @@ END_MLAPI_EXCLUDE def_API('Z3_func_decl_to_ast', AST, (_in(CONTEXT), _in(FUNC_DECL))) */ - Z3_ast Z3_API Z3_func_decl_to_ast(__in Z3_context c, __in Z3_func_decl f); + Z3_ast Z3_API Z3_func_decl_to_ast(Z3_context c, Z3_func_decl f); /** \brief compare terms. @@ -3911,7 +3985,7 @@ END_MLAPI_EXCLUDE def_API('Z3_is_eq_func_decl', BOOL, (_in(CONTEXT), _in(FUNC_DECL), _in(FUNC_DECL))) */ - Z3_bool Z3_API Z3_is_eq_func_decl(__in Z3_context c, __in Z3_func_decl f1, Z3_func_decl f2); + Z3_bool Z3_API Z3_is_eq_func_decl(Z3_context c, Z3_func_decl f1, Z3_func_decl f2); /** \brief Return a unique identifier for \c f. @@ -3919,21 +3993,21 @@ END_MLAPI_EXCLUDE def_API('Z3_get_func_decl_id', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ - unsigned Z3_API Z3_get_func_decl_id(__in Z3_context c, Z3_func_decl f); + unsigned Z3_API Z3_get_func_decl_id(Z3_context c, Z3_func_decl f); /** \brief Return the constant declaration name as a symbol. def_API('Z3_get_decl_name', SYMBOL, (_in(CONTEXT), _in(FUNC_DECL))) */ - Z3_symbol Z3_API Z3_get_decl_name(__in Z3_context c, __in Z3_func_decl d); + Z3_symbol Z3_API Z3_get_decl_name(Z3_context c, Z3_func_decl d); /** \brief Return declaration kind corresponding to declaration. def_API('Z3_get_decl_kind', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ - Z3_decl_kind Z3_API Z3_get_decl_kind(__in Z3_context c, __in Z3_func_decl d); + Z3_decl_kind Z3_API Z3_get_decl_kind(Z3_context c, Z3_func_decl d); /** \brief Return the number of parameters of the given declaration. @@ -3942,7 +4016,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_domain_size', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ - unsigned Z3_API Z3_get_domain_size(__in Z3_context c, __in Z3_func_decl d); + unsigned Z3_API Z3_get_domain_size(Z3_context c, Z3_func_decl d); /** \brief Alias for \c Z3_get_domain_size. @@ -3951,7 +4025,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_arity', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ - unsigned Z3_API Z3_get_arity(__in Z3_context c, __in Z3_func_decl d); + unsigned Z3_API Z3_get_arity(Z3_context c, Z3_func_decl d); /** \brief \mlh get_domain c d i \endmlh @@ -3963,7 +4037,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_domain', SORT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - Z3_sort Z3_API Z3_get_domain(__in Z3_context c, __in Z3_func_decl d, __in unsigned i); + Z3_sort Z3_API Z3_get_domain(Z3_context c, Z3_func_decl d, unsigned i); #ifdef ML4only #include @@ -3978,14 +4052,14 @@ END_MLAPI_EXCLUDE def_API('Z3_get_range', SORT, (_in(CONTEXT), _in(FUNC_DECL))) */ - Z3_sort Z3_API Z3_get_range(__in Z3_context c, __in Z3_func_decl d); + Z3_sort Z3_API Z3_get_range(Z3_context c, Z3_func_decl d); /** \brief Return the number of parameters associated with a declaration. def_API('Z3_get_decl_num_parameters', UINT, (_in(CONTEXT), _in(FUNC_DECL))) */ - unsigned Z3_API Z3_get_decl_num_parameters(__in Z3_context c, __in Z3_func_decl d); + unsigned Z3_API Z3_get_decl_num_parameters(Z3_context c, Z3_func_decl d); /** \brief Return the parameter type associated with a declaration. @@ -3996,7 +4070,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_decl_parameter_kind', UINT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - Z3_parameter_kind Z3_API Z3_get_decl_parameter_kind(__in Z3_context c, __in Z3_func_decl d, unsigned idx); + Z3_parameter_kind Z3_API Z3_get_decl_parameter_kind(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the integer value associated with an integer parameter. @@ -4005,7 +4079,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_decl_int_parameter', INT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - int Z3_API Z3_get_decl_int_parameter(__in Z3_context c, __in Z3_func_decl d, unsigned idx); + int Z3_API Z3_get_decl_int_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the double value associated with an double parameter. @@ -4014,7 +4088,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_decl_double_parameter', DOUBLE, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - double Z3_API Z3_get_decl_double_parameter(__in Z3_context c, __in Z3_func_decl d, unsigned idx); + double Z3_API Z3_get_decl_double_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the double value associated with an double parameter. @@ -4023,7 +4097,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_decl_symbol_parameter', SYMBOL, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - Z3_symbol Z3_API Z3_get_decl_symbol_parameter(__in Z3_context c, __in Z3_func_decl d, unsigned idx); + Z3_symbol Z3_API Z3_get_decl_symbol_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the sort value associated with a sort parameter. @@ -4032,7 +4106,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_decl_sort_parameter', SORT, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - Z3_sort Z3_API Z3_get_decl_sort_parameter(__in Z3_context c, __in Z3_func_decl d, unsigned idx); + Z3_sort Z3_API Z3_get_decl_sort_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the expresson value associated with an expression parameter. @@ -4041,7 +4115,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_decl_ast_parameter', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - Z3_ast Z3_API Z3_get_decl_ast_parameter(__in Z3_context c, __in Z3_func_decl d, unsigned idx); + Z3_ast Z3_API Z3_get_decl_ast_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the expresson value associated with an expression parameter. @@ -4050,7 +4124,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_decl_func_decl_parameter', FUNC_DECL, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - Z3_func_decl Z3_API Z3_get_decl_func_decl_parameter(__in Z3_context c, __in Z3_func_decl d, unsigned idx); + Z3_func_decl Z3_API Z3_get_decl_func_decl_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \brief Return the rational value, as a string, associated with a rational parameter. @@ -4059,7 +4133,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_decl_rational_parameter', STRING, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT))) */ - Z3_string Z3_API Z3_get_decl_rational_parameter(__in Z3_context c, __in Z3_func_decl d, unsigned idx); + Z3_string Z3_API Z3_get_decl_rational_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** \mlonly {3 {L Applications}} \endmlonly @@ -4071,14 +4145,14 @@ END_MLAPI_EXCLUDE def_API('Z3_app_to_ast', AST, (_in(CONTEXT), _in(APP))) */ - Z3_ast Z3_API Z3_app_to_ast(__in Z3_context c, __in Z3_app a); + Z3_ast Z3_API Z3_app_to_ast(Z3_context c, Z3_app a); /** \brief Return the declaration of a constant or function application. def_API('Z3_get_app_decl', FUNC_DECL, (_in(CONTEXT), _in(APP))) */ - Z3_func_decl Z3_API Z3_get_app_decl(__in Z3_context c, __in Z3_app a); + Z3_func_decl Z3_API Z3_get_app_decl(Z3_context c, Z3_app a); /** \brief \mlh get_app_num_args c a \endmlh @@ -4087,7 +4161,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_app_num_args', UINT, (_in(CONTEXT), _in(APP))) */ - unsigned Z3_API Z3_get_app_num_args(__in Z3_context c, __in Z3_app a); + unsigned Z3_API Z3_get_app_num_args(Z3_context c, Z3_app a); /** \brief \mlh get_app_arg c a i \endmlh @@ -4097,7 +4171,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_app_arg', AST, (_in(CONTEXT), _in(APP), _in(UINT))) */ - Z3_ast Z3_API Z3_get_app_arg(__in Z3_context c, __in Z3_app a, __in unsigned i); + Z3_ast Z3_API Z3_get_app_arg(Z3_context c, Z3_app a, unsigned i); #ifdef ML4only #include @@ -4118,7 +4192,7 @@ END_MLAPI_EXCLUDE def_API('Z3_is_eq_ast', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) */ - Z3_bool Z3_API Z3_is_eq_ast(__in Z3_context c, __in Z3_ast t1, Z3_ast t2); + Z3_bool Z3_API Z3_is_eq_ast(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Return a unique identifier for \c t. @@ -4132,7 +4206,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_ast_id', UINT, (_in(CONTEXT), _in(AST))) */ - unsigned Z3_API Z3_get_ast_id(__in Z3_context c, Z3_ast t); + unsigned Z3_API Z3_get_ast_id(Z3_context c, Z3_ast t); /** \brief Return a hash code for the given AST. @@ -4142,7 +4216,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_ast_hash', UINT, (_in(CONTEXT), _in(AST))) */ - unsigned Z3_API Z3_get_ast_hash(__in Z3_context c, __in Z3_ast a); + unsigned Z3_API Z3_get_ast_hash(Z3_context c, Z3_ast a); /** \brief Return the sort of an AST node. @@ -4151,45 +4225,45 @@ END_MLAPI_EXCLUDE def_API('Z3_get_sort', SORT, (_in(CONTEXT), _in(AST))) */ - Z3_sort Z3_API Z3_get_sort(__in Z3_context c, __in Z3_ast a); + Z3_sort Z3_API Z3_get_sort(Z3_context c, Z3_ast a); /** \brief Return true if the given expression \c t is well sorted. def_API('Z3_is_well_sorted', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_is_well_sorted(__in Z3_context c, __in Z3_ast t); + Z3_bool Z3_API Z3_is_well_sorted(Z3_context c, Z3_ast t); /** \brief Return Z3_L_TRUE if \c a is true, Z3_L_FALSE if it is false, and Z3_L_UNDEF otherwise. def_API('Z3_get_bool_value', UINT, (_in(CONTEXT), _in(AST))) */ - Z3_lbool Z3_API Z3_get_bool_value(__in Z3_context c, __in Z3_ast a); + Z3_lbool Z3_API Z3_get_bool_value(Z3_context c, Z3_ast a); /** \brief Return the kind of the given AST. def_API('Z3_get_ast_kind', UINT, (_in(CONTEXT), _in(AST))) */ - Z3_ast_kind Z3_API Z3_get_ast_kind(__in Z3_context c, __in Z3_ast a); + Z3_ast_kind Z3_API Z3_get_ast_kind(Z3_context c, Z3_ast a); /** def_API('Z3_is_app', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_is_app(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_is_app(Z3_context c, Z3_ast a); /** def_API('Z3_is_numeral_ast', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_is_numeral_ast(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_is_numeral_ast(Z3_context c, Z3_ast a); /** \brief Return true if the give AST is a real algebraic number. def_API('Z3_is_algebraic_number', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_is_algebraic_number(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_is_algebraic_number(Z3_context c, Z3_ast a); /** \brief Convert an \c ast into an \c APP_AST. \conly This is just type casting. @@ -4198,7 +4272,7 @@ END_MLAPI_EXCLUDE def_API('Z3_to_app', APP, (_in(CONTEXT), _in(AST))) */ - Z3_app Z3_API Z3_to_app(__in Z3_context c, __in Z3_ast a); + Z3_app Z3_API Z3_to_app(Z3_context c, Z3_ast a); /** \brief Convert an AST into a FUNC_DECL_AST. This is just type casting. @@ -4207,7 +4281,7 @@ END_MLAPI_EXCLUDE def_API('Z3_to_func_decl', FUNC_DECL, (_in(CONTEXT), _in(AST))) */ - Z3_func_decl Z3_API Z3_to_func_decl(__in Z3_context c, __in Z3_ast a); + Z3_func_decl Z3_API Z3_to_func_decl(Z3_context c, Z3_ast a); /** @@ -4229,7 +4303,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numeral_string', STRING, (_in(CONTEXT), _in(AST))) */ - Z3_string Z3_API Z3_get_numeral_string(__in Z3_context c, __in Z3_ast a); + Z3_string Z3_API Z3_get_numeral_string(Z3_context c, Z3_ast a); /** \brief Return numeral as a string in decimal notation. @@ -4239,7 +4313,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numeral_decimal_string', STRING, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_string Z3_API Z3_get_numeral_decimal_string(__in Z3_context c, __in Z3_ast a, __in unsigned precision); + Z3_string Z3_API Z3_get_numeral_decimal_string(Z3_context c, Z3_ast a, unsigned precision); /** \brief Return the numerator (as a numeral AST) of a numeral AST of sort Real. @@ -4248,7 +4322,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numerator', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_get_numerator(__in Z3_context c, __in Z3_ast a); + Z3_ast Z3_API Z3_get_numerator(Z3_context c, Z3_ast a); /** \brief Return the denominator (as a numeral AST) of a numeral AST of sort Real. @@ -4257,7 +4331,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_denominator', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_get_denominator(__in Z3_context c, __in Z3_ast a); + Z3_ast Z3_API Z3_get_denominator(Z3_context c, Z3_ast a); /** \brief Return numeral value, as a pair of 64 bit numbers if the representation fits. @@ -4273,7 +4347,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numeral_small', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _out(INT64))) */ - Z3_bool Z3_API Z3_get_numeral_small(__in Z3_context c, __in Z3_ast a, __out __int64* num, __out __int64* den); + Z3_bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, __int64* num, __int64* den); /** \brief \mlh get_numeral_int c v \endmlh @@ -4286,7 +4360,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numeral_int', BOOL, (_in(CONTEXT), _in(AST), _out(INT))) */ - Z3_bool Z3_API Z3_get_numeral_int(__in Z3_context c, __in Z3_ast v, __out int* i); + Z3_bool Z3_API Z3_get_numeral_int(Z3_context c, Z3_ast v, int* i); #ifdef Conly /** @@ -4300,7 +4374,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numeral_uint', BOOL, (_in(CONTEXT), _in(AST), _out(UINT))) */ - Z3_bool Z3_API Z3_get_numeral_uint(__in Z3_context c, __in Z3_ast v, __out unsigned* u); + Z3_bool Z3_API Z3_get_numeral_uint(Z3_context c, Z3_ast v, unsigned* u); #endif #ifdef Conly @@ -4315,7 +4389,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numeral_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ - Z3_bool Z3_API Z3_get_numeral_uint64(__in Z3_context c, __in Z3_ast v, __out unsigned __int64* u); + Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, unsigned __int64* u); #endif /** @@ -4329,7 +4403,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numeral_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64))) */ - Z3_bool Z3_API Z3_get_numeral_int64(__in Z3_context c, __in Z3_ast v, __out __int64* i); + Z3_bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, __int64* i); /** \brief \mlh get_numeral_rational_int64 c x y\endmlh @@ -4342,7 +4416,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_numeral_rational_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _out(INT64))) */ - Z3_bool Z3_API Z3_get_numeral_rational_int64(__in Z3_context c, __in Z3_ast v, __out __int64* num, __out __int64* den); + Z3_bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, __int64* num, __int64* den); /** \brief Return a lower bound for the given real algebraic number. @@ -4353,7 +4427,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_algebraic_number_lower', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_ast Z3_API Z3_get_algebraic_number_lower(__in Z3_context c, __in Z3_ast a, __in unsigned precision); + Z3_ast Z3_API Z3_get_algebraic_number_lower(Z3_context c, Z3_ast a, unsigned precision); /** \brief Return a upper bound for the given real algebraic number. @@ -4377,7 +4451,7 @@ END_MLAPI_EXCLUDE def_API('Z3_pattern_to_ast', AST, (_in(CONTEXT), _in(PATTERN))) */ - Z3_ast Z3_API Z3_pattern_to_ast(__in Z3_context c, __in Z3_pattern p); + Z3_ast Z3_API Z3_pattern_to_ast(Z3_context c, Z3_pattern p); #ifdef ML4only #include @@ -4388,14 +4462,14 @@ END_MLAPI_EXCLUDE def_API('Z3_get_pattern_num_terms', UINT, (_in(CONTEXT), _in(PATTERN))) */ - unsigned Z3_API Z3_get_pattern_num_terms(__in Z3_context c, __in Z3_pattern p); + unsigned Z3_API Z3_get_pattern_num_terms(Z3_context c, Z3_pattern p); /** \brief Return i'th ast in pattern. def_API('Z3_get_pattern', AST, (_in(CONTEXT), _in(PATTERN), _in(UINT))) */ - Z3_ast Z3_API Z3_get_pattern(__in Z3_context c, __in Z3_pattern p, __in unsigned idx); + Z3_ast Z3_API Z3_get_pattern(Z3_context c, Z3_pattern p, unsigned idx); /** @@ -4409,7 +4483,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_index_value', UINT, (_in(CONTEXT), _in(AST))) */ - unsigned Z3_API Z3_get_index_value(__in Z3_context c, __in Z3_ast a); + unsigned Z3_API Z3_get_index_value(Z3_context c, Z3_ast a); /** \brief Determine if quantifier is universal. @@ -4418,7 +4492,7 @@ END_MLAPI_EXCLUDE def_API('Z3_is_quantifier_forall', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_is_quantifier_forall(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_is_quantifier_forall(Z3_context c, Z3_ast a); /** \brief Obtain weight of quantifier. @@ -4427,7 +4501,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_weight', UINT, (_in(CONTEXT), _in(AST))) */ - unsigned Z3_API Z3_get_quantifier_weight(__in Z3_context c, __in Z3_ast a); + unsigned Z3_API Z3_get_quantifier_weight(Z3_context c, Z3_ast a); /** \brief Return number of patterns used in quantifier. @@ -4436,7 +4510,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_num_patterns', UINT, (_in(CONTEXT), _in(AST))) */ - unsigned Z3_API Z3_get_quantifier_num_patterns(__in Z3_context c, __in Z3_ast a); + unsigned Z3_API Z3_get_quantifier_num_patterns(Z3_context c, Z3_ast a); /** \brief Return i'th pattern. @@ -4445,7 +4519,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_pattern_ast', PATTERN, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_pattern Z3_API Z3_get_quantifier_pattern_ast(__in Z3_context c, __in Z3_ast a, unsigned i); + Z3_pattern Z3_API Z3_get_quantifier_pattern_ast(Z3_context c, Z3_ast a, unsigned i); /** \brief Return number of no_patterns used in quantifier. @@ -4454,7 +4528,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_num_no_patterns', UINT, (_in(CONTEXT), _in(AST))) */ - unsigned Z3_API Z3_get_quantifier_num_no_patterns(__in Z3_context c, __in Z3_ast a); + unsigned Z3_API Z3_get_quantifier_num_no_patterns(Z3_context c, Z3_ast a); /** \brief Return i'th no_pattern. @@ -4463,7 +4537,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_no_pattern_ast', AST, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_ast Z3_API Z3_get_quantifier_no_pattern_ast(__in Z3_context c, __in Z3_ast a, unsigned i); + Z3_ast Z3_API Z3_get_quantifier_no_pattern_ast(Z3_context c, Z3_ast a, unsigned i); /** \brief Return number of bound variables of quantifier. @@ -4472,7 +4546,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_num_bound', UINT, (_in(CONTEXT), _in(AST))) */ - unsigned Z3_API Z3_get_quantifier_num_bound(__in Z3_context c, __in Z3_ast a); + unsigned Z3_API Z3_get_quantifier_num_bound(Z3_context c, Z3_ast a); /** \brief Return symbol of the i'th bound variable. @@ -4481,7 +4555,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_bound_name', SYMBOL, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_symbol Z3_API Z3_get_quantifier_bound_name(__in Z3_context c, __in Z3_ast a, unsigned i); + Z3_symbol Z3_API Z3_get_quantifier_bound_name(Z3_context c, Z3_ast a, unsigned i); /** \brief Return sort of the i'th bound variable. @@ -4490,7 +4564,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_bound_sort', SORT, (_in(CONTEXT), _in(AST), _in(UINT))) */ - Z3_sort Z3_API Z3_get_quantifier_bound_sort(__in Z3_context c, __in Z3_ast a, unsigned i); + Z3_sort Z3_API Z3_get_quantifier_bound_sort(Z3_context c, Z3_ast a, unsigned i); /** \brief Return body of quantifier. @@ -4499,7 +4573,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_quantifier_body', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_get_quantifier_body(__in Z3_context c, __in Z3_ast a); + Z3_ast Z3_API Z3_get_quantifier_body(Z3_context c, Z3_ast a); /** @@ -4513,7 +4587,7 @@ END_MLAPI_EXCLUDE def_API('Z3_simplify', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_simplify(__in Z3_context c, __in Z3_ast a); + Z3_ast Z3_API Z3_simplify(Z3_context c, Z3_ast a); #ifdef CorML4 /** @@ -4525,21 +4599,21 @@ END_MLAPI_EXCLUDE def_API('Z3_simplify_ex', AST, (_in(CONTEXT), _in(AST), _in(PARAMS))) */ - Z3_ast Z3_API Z3_simplify_ex(__in Z3_context c, __in Z3_ast a, __in Z3_params p); + Z3_ast Z3_API Z3_simplify_ex(Z3_context c, Z3_ast a, Z3_params p); /** \brief Return a string describing all available parameters. def_API('Z3_simplify_get_help', STRING, (_in(CONTEXT),)) */ - Z3_string Z3_API Z3_simplify_get_help(__in Z3_context c); + Z3_string Z3_API Z3_simplify_get_help(Z3_context c); /** \brief Return the parameter description set for the simplify procedure. def_API('Z3_simplify_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT),)) */ - Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(__in Z3_context c); + Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c); #endif /*@}*/ @@ -4557,7 +4631,7 @@ END_MLAPI_EXCLUDE def_API('Z3_update_term', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ - Z3_ast Z3_API Z3_update_term(__in Z3_context c, __in Z3_ast a, __in unsigned num_args, __in_ecount(num_args) Z3_ast const args[]); + Z3_ast Z3_API Z3_update_term(Z3_context c, Z3_ast a, unsigned num_args, Z3_ast const args[]); /** \brief Substitute every occurrence of from[i] in \c a with to[i], for \c i smaller than \c num_exprs. @@ -4566,11 +4640,11 @@ END_MLAPI_EXCLUDE def_API('Z3_substitute', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST), _in_array(2, AST))) */ - Z3_ast Z3_API Z3_substitute(__in Z3_context c, - __in Z3_ast a, - __in unsigned num_exprs, - __in_ecount(num_exprs) Z3_ast const from[], - __in_ecount(num_exprs) Z3_ast const to[]); + Z3_ast Z3_API Z3_substitute(Z3_context c, + Z3_ast a, + unsigned num_exprs, + Z3_ast const from[], + Z3_ast const to[]); /** \brief Substitute the free variables in \c a with the expressions in \c to. @@ -4578,10 +4652,10 @@ END_MLAPI_EXCLUDE def_API('Z3_substitute_vars', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) */ - Z3_ast Z3_API Z3_substitute_vars(__in Z3_context c, - __in Z3_ast a, - __in unsigned num_exprs, - __in_ecount(num_exprs) Z3_ast const to[]); + Z3_ast Z3_API Z3_substitute_vars(Z3_context c, + Z3_ast a, + unsigned num_exprs, + Z3_ast const to[]); #ifdef CorML4 /** @@ -4591,7 +4665,7 @@ END_MLAPI_EXCLUDE def_API('Z3_translate', AST, (_in(CONTEXT), _in(AST), _in(CONTEXT))) */ - Z3_ast Z3_API Z3_translate(__in Z3_context source, __in Z3_ast a, __in Z3_context target); + Z3_ast Z3_API Z3_translate(Z3_context source, Z3_ast a, Z3_context target); #endif /*@}*/ @@ -4611,14 +4685,14 @@ END_MLAPI_EXCLUDE def_API('Z3_model_inc_ref', VOID, (_in(CONTEXT), _in(MODEL))) */ - void Z3_API Z3_model_inc_ref(__in Z3_context c, __in Z3_model m); + void Z3_API Z3_model_inc_ref(Z3_context c, Z3_model m); /** \brief Decrement the reference counter of the given model. def_API('Z3_model_dec_ref', VOID, (_in(CONTEXT), _in(MODEL))) */ - void Z3_API Z3_model_dec_ref(__in Z3_context c, __in Z3_model m); + void Z3_API Z3_model_dec_ref(Z3_context c, Z3_model m); #endif /** @@ -4641,7 +4715,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_eval', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _in(BOOL), _out(AST))) */ - Z3_bool_opt Z3_API Z3_model_eval(__in Z3_context c, __in Z3_model m, __in Z3_ast t, __in Z3_bool model_completion, __out_opt Z3_ast * v); + Z3_bool_opt Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, Z3_bool model_completion, Z3_ast * v); /** \mlonly {4 {L Low-level API}} \endmlonly @@ -4657,14 +4731,14 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_const_interp', AST, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ - Z3_ast_opt Z3_API Z3_model_get_const_interp(__in Z3_context c, __in Z3_model m, __in Z3_func_decl a); + Z3_ast_opt Z3_API Z3_model_get_const_interp(Z3_context c, Z3_model m, Z3_func_decl a); /** \brief Test if there exists an interpretation (i.e., assignment) for \c a in the model \c m. def_API('Z3_model_has_interp', BOOL, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ - Z3_bool Z3_API Z3_model_has_interp(__in Z3_context c, __in Z3_model m, __in Z3_func_decl a); + Z3_bool Z3_API Z3_model_has_interp(Z3_context c, Z3_model m, Z3_func_decl a); /** \brief Return the interpretation of the function \c f in the model \c m. @@ -4679,7 +4753,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_func_interp', FUNC_INTERP, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL))) */ - Z3_func_interp_opt Z3_API Z3_model_get_func_interp(__in Z3_context c, __in Z3_model m, __in Z3_func_decl f); + Z3_func_interp_opt Z3_API Z3_model_get_func_interp(Z3_context c, Z3_model m, Z3_func_decl f); /** \brief Return the number of constants assigned by the given model. @@ -4688,7 +4762,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_num_consts', UINT, (_in(CONTEXT), _in(MODEL))) */ - unsigned Z3_API Z3_model_get_num_consts(__in Z3_context c, __in Z3_model m); + unsigned Z3_API Z3_model_get_num_consts(Z3_context c, Z3_model m); /** \brief \mlh model_get_const_decl c m i \endmlh @@ -4700,7 +4774,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_const_decl', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ - Z3_func_decl Z3_API Z3_model_get_const_decl(__in Z3_context c, __in Z3_model m, __in unsigned i); + Z3_func_decl Z3_API Z3_model_get_const_decl(Z3_context c, Z3_model m, unsigned i); /** \brief Return the number of function interpretations in the given model. @@ -4710,7 +4784,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_num_funcs', UINT, (_in(CONTEXT), _in(MODEL))) */ - unsigned Z3_API Z3_model_get_num_funcs(__in Z3_context c, __in Z3_model m); + unsigned Z3_API Z3_model_get_num_funcs(Z3_context c, Z3_model m); /** \brief \mlh model_get_func_decl c m i \endmlh @@ -4722,7 +4796,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_func_decl', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ - Z3_func_decl Z3_API Z3_model_get_func_decl(__in Z3_context c, __in Z3_model m, __in unsigned i); + Z3_func_decl Z3_API Z3_model_get_func_decl(Z3_context c, Z3_model m, unsigned i); /** \brief Return the number of uninterpreted sorts that \c m assigs an interpretation to. @@ -4736,7 +4810,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_num_sorts', UINT, (_in(CONTEXT), _in(MODEL))) */ - unsigned Z3_API Z3_model_get_num_sorts(__in Z3_context c, __in Z3_model m); + unsigned Z3_API Z3_model_get_num_sorts(Z3_context c, Z3_model m); /** \brief Return a uninterpreted sort that \c m assigns an interpretation. @@ -4748,7 +4822,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_sort', SORT, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ - Z3_sort Z3_API Z3_model_get_sort(__in Z3_context c, __in Z3_model m, __in unsigned i); + Z3_sort Z3_API Z3_model_get_sort(Z3_context c, Z3_model m, unsigned i); /** \brief Return the finite set of distinct values that represent the interpretation for sort \c s. @@ -4758,7 +4832,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_get_sort_universe', AST_VECTOR, (_in(CONTEXT), _in(MODEL), _in(SORT))) */ - Z3_ast_vector Z3_API Z3_model_get_sort_universe(__in Z3_context c, __in Z3_model m, __in Z3_sort s); + Z3_ast_vector Z3_API Z3_model_get_sort_universe(Z3_context c, Z3_model m, Z3_sort s); /** \brief The (_ as-array f) AST node is a construct for assigning interpretations for arrays in Z3. @@ -4771,7 +4845,7 @@ END_MLAPI_EXCLUDE def_API('Z3_is_as_array', BOOL, (_in(CONTEXT), _in(AST))) */ - Z3_bool Z3_API Z3_is_as_array(__in Z3_context c, __in Z3_ast a); + Z3_bool Z3_API Z3_is_as_array(Z3_context c, Z3_ast a); /** \brief Return the function declaration \c f associated with a (_ as_array f) node. @@ -4780,7 +4854,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_as_array_func_decl', FUNC_DECL, (_in(CONTEXT), _in(AST))) */ - Z3_func_decl Z3_API Z3_get_as_array_func_decl(__in Z3_context c, __in Z3_ast a); + Z3_func_decl Z3_API Z3_get_as_array_func_decl(Z3_context c, Z3_ast a); #ifdef Conly /** @@ -4788,14 +4862,14 @@ END_MLAPI_EXCLUDE def_API('Z3_func_interp_inc_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP))) */ - void Z3_API Z3_func_interp_inc_ref(__in Z3_context c, __in Z3_func_interp f); + void Z3_API Z3_func_interp_inc_ref(Z3_context c, Z3_func_interp f); /** \brief Decrement the reference counter of the given Z3_func_interp object. def_API('Z3_func_interp_dec_ref', VOID, (_in(CONTEXT), _in(FUNC_INTERP))) */ - void Z3_API Z3_func_interp_dec_ref(__in Z3_context c, __in Z3_func_interp f); + void Z3_API Z3_func_interp_dec_ref(Z3_context c, Z3_func_interp f); #endif /** @@ -4807,7 +4881,7 @@ END_MLAPI_EXCLUDE def_API('Z3_func_interp_get_num_entries', UINT, (_in(CONTEXT), _in(FUNC_INTERP))) */ - unsigned Z3_API Z3_func_interp_get_num_entries(__in Z3_context c, __in Z3_func_interp f); + unsigned Z3_API Z3_func_interp_get_num_entries(Z3_context c, Z3_func_interp f); /** \brief Return a "point" of the given function intepretation. It represents the @@ -4819,7 +4893,7 @@ END_MLAPI_EXCLUDE def_API('Z3_func_interp_get_entry', FUNC_ENTRY, (_in(CONTEXT), _in(FUNC_INTERP), _in(UINT))) */ - Z3_func_entry Z3_API Z3_func_interp_get_entry(__in Z3_context c, __in Z3_func_interp f, unsigned i); + Z3_func_entry Z3_API Z3_func_interp_get_entry(Z3_context c, Z3_func_interp f, unsigned i); /** \brief Return the 'else' value of the given function interpretation. @@ -4829,14 +4903,14 @@ END_MLAPI_EXCLUDE def_API('Z3_func_interp_get_else', AST, (_in(CONTEXT), _in(FUNC_INTERP))) */ - Z3_ast Z3_API Z3_func_interp_get_else(__in Z3_context c, __in Z3_func_interp f); + Z3_ast Z3_API Z3_func_interp_get_else(Z3_context c, Z3_func_interp f); /** \brief Return the arity (number of arguments) of the given function interpretation. def_API('Z3_func_interp_get_arity', UINT, (_in(CONTEXT), _in(FUNC_INTERP))) */ - unsigned Z3_API Z3_func_interp_get_arity(__in Z3_context c, __in Z3_func_interp f); + unsigned Z3_API Z3_func_interp_get_arity(Z3_context c, Z3_func_interp f); #ifdef Conly /** @@ -4844,14 +4918,14 @@ END_MLAPI_EXCLUDE def_API('Z3_func_entry_inc_ref', VOID, (_in(CONTEXT), _in(FUNC_ENTRY))) */ - void Z3_API Z3_func_entry_inc_ref(__in Z3_context c, __in Z3_func_entry e); + void Z3_API Z3_func_entry_inc_ref(Z3_context c, Z3_func_entry e); /** \brief Decrement the reference counter of the given Z3_func_entry object. def_API('Z3_func_entry_dec_ref', VOID, (_in(CONTEXT), _in(FUNC_ENTRY))) */ - void Z3_API Z3_func_entry_dec_ref(__in Z3_context c, __in Z3_func_entry e); + void Z3_API Z3_func_entry_dec_ref(Z3_context c, Z3_func_entry e); #endif /** @@ -4864,7 +4938,7 @@ END_MLAPI_EXCLUDE def_API('Z3_func_entry_get_value', AST, (_in(CONTEXT), _in(FUNC_ENTRY))) */ - Z3_ast Z3_API Z3_func_entry_get_value(__in Z3_context c, __in Z3_func_entry e); + Z3_ast Z3_API Z3_func_entry_get_value(Z3_context c, Z3_func_entry e); /** \brief Return the number of arguments in a Z3_func_entry object. @@ -4873,7 +4947,7 @@ END_MLAPI_EXCLUDE def_API('Z3_func_entry_get_num_args', UINT, (_in(CONTEXT), _in(FUNC_ENTRY))) */ - unsigned Z3_API Z3_func_entry_get_num_args(__in Z3_context c, __in Z3_func_entry e); + unsigned Z3_API Z3_func_entry_get_num_args(Z3_context c, Z3_func_entry e); /** \brief Return an argument of a Z3_func_entry object. @@ -4884,7 +4958,7 @@ END_MLAPI_EXCLUDE def_API('Z3_func_entry_get_arg', AST, (_in(CONTEXT), _in(FUNC_ENTRY), _in(UINT))) */ - Z3_ast Z3_API Z3_func_entry_get_arg(__in Z3_context c, __in Z3_func_entry e, __in unsigned i); + Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i); /*@}*/ #endif // CorML4 @@ -4899,7 +4973,7 @@ END_MLAPI_EXCLUDE extra_API('Z3_open_log', INT, (_in(STRING),)) */ - Z3_bool Z3_API Z3_open_log(__in Z3_string filename); + Z3_bool Z3_API Z3_open_log(Z3_string filename); /** \brief Append user-defined string to interaction log. @@ -4910,7 +4984,7 @@ END_MLAPI_EXCLUDE extra_API('Z3_append_log', VOID, (_in(STRING),)) */ - void Z3_API Z3_append_log(__in Z3_string string); + void Z3_API Z3_append_log(Z3_string string); /** \brief Close interaction log. @@ -4927,7 +5001,7 @@ END_MLAPI_EXCLUDE def_API('Z3_toggle_warning_messages', VOID, (_in(BOOL),)) */ - void Z3_API Z3_toggle_warning_messages(__in Z3_bool enabled); + void Z3_API Z3_toggle_warning_messages(Z3_bool enabled); /*@}*/ @@ -4953,7 +5027,7 @@ END_MLAPI_EXCLUDE def_API('Z3_set_ast_print_mode', VOID, (_in(CONTEXT), _in(PRINT_MODE))) */ - void Z3_API Z3_set_ast_print_mode(__in Z3_context c, __in Z3_ast_print_mode mode); + void Z3_API Z3_set_ast_print_mode(Z3_context c, Z3_ast_print_mode mode); /** \brief Convert the given AST node into a string. @@ -4966,22 +5040,22 @@ END_MLAPI_EXCLUDE def_API('Z3_ast_to_string', STRING, (_in(CONTEXT), _in(AST))) */ - Z3_string Z3_API Z3_ast_to_string(__in Z3_context c, __in Z3_ast a); + Z3_string Z3_API Z3_ast_to_string(Z3_context c, Z3_ast a); /** def_API('Z3_pattern_to_string', STRING, (_in(CONTEXT), _in(PATTERN))) */ - Z3_string Z3_API Z3_pattern_to_string(__in Z3_context c, __in Z3_pattern p); + Z3_string Z3_API Z3_pattern_to_string(Z3_context c, Z3_pattern p); /** def_API('Z3_sort_to_string', STRING, (_in(CONTEXT), _in(SORT))) */ - Z3_string Z3_API Z3_sort_to_string(__in Z3_context c, __in Z3_sort s); + Z3_string Z3_API Z3_sort_to_string(Z3_context c, Z3_sort s); /** def_API('Z3_func_decl_to_string', STRING, (_in(CONTEXT), _in(FUNC_DECL))) */ - Z3_string Z3_API Z3_func_decl_to_string(__in Z3_context c, __in Z3_func_decl d); + Z3_string Z3_API Z3_func_decl_to_string(Z3_context c, Z3_func_decl d); /** \brief Convert the given model into a string. @@ -4992,7 +5066,7 @@ END_MLAPI_EXCLUDE def_API('Z3_model_to_string', STRING, (_in(CONTEXT), _in(MODEL))) */ - Z3_string Z3_API Z3_model_to_string(__in Z3_context c, __in Z3_model m); + Z3_string Z3_API Z3_model_to_string(Z3_context c, Z3_model m); /** \brief Convert the given benchmark into SMT-LIB formatted string. @@ -5012,14 +5086,14 @@ END_MLAPI_EXCLUDE def_API('Z3_benchmark_to_smtlib_string', STRING, (_in(CONTEXT), _in(STRING), _in(STRING), _in(STRING), _in(STRING), _in(UINT), _in_array(5, AST), _in(AST))) */ - Z3_string Z3_API Z3_benchmark_to_smtlib_string(__in Z3_context c, - __in Z3_string name, - __in Z3_string logic, - __in Z3_string status, - __in Z3_string attributes, - __in unsigned num_assumptions, - __in_ecount(num_assumptions) Z3_ast const assumptions[], - __in Z3_ast formula); + Z3_string Z3_API Z3_benchmark_to_smtlib_string(Z3_context c, + Z3_string name, + Z3_string logic, + Z3_string status, + Z3_string attributes, + unsigned num_assumptions, + Z3_ast const assumptions[], + Z3_ast formula); /*@}*/ @@ -5037,28 +5111,28 @@ END_MLAPI_EXCLUDE def_API('Z3_parse_smtlib2_string', AST, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ - Z3_ast Z3_API Z3_parse_smtlib2_string(__in Z3_context c, - __in Z3_string str, - __in unsigned num_sorts, - __in_ecount(num_sorts) Z3_symbol const sort_names[], - __in_ecount(num_sorts) Z3_sort const sorts[], - __in unsigned num_decls, - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in_ecount(num_decls) Z3_func_decl const decls[]); + Z3_ast Z3_API Z3_parse_smtlib2_string(Z3_context c, + Z3_string str, + unsigned num_sorts, + Z3_symbol const sort_names[], + Z3_sort const sorts[], + unsigned num_decls, + Z3_symbol const decl_names[], + Z3_func_decl const decls[]); /** \brief Similar to #Z3_parse_smtlib2_string, but reads the benchmark from a file. def_API('Z3_parse_smtlib2_file', AST, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ - Z3_ast Z3_API Z3_parse_smtlib2_file(__in Z3_context c, - __in Z3_string file_name, - __in unsigned num_sorts, - __in_ecount(num_sorts) Z3_symbol const sort_names[], - __in_ecount(num_sorts) Z3_sort const sorts[], - __in unsigned num_decls, - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in_ecount(num_decls) Z3_func_decl const decls[]); + Z3_ast Z3_API Z3_parse_smtlib2_file(Z3_context c, + Z3_string file_name, + unsigned num_sorts, + Z3_symbol const sort_names[], + Z3_sort const sorts[], + unsigned num_decls, + Z3_symbol const decl_names[], + Z3_func_decl const decls[]); #ifdef ML4only #include @@ -5083,14 +5157,14 @@ END_MLAPI_EXCLUDE def_API('Z3_parse_smtlib_string', VOID, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ - void Z3_API Z3_parse_smtlib_string(__in Z3_context c, - __in Z3_string str, - __in unsigned num_sorts, - __in_ecount(num_sorts) Z3_symbol const sort_names[], - __in_ecount(num_sorts) Z3_sort const sorts[], - __in unsigned num_decls, - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in_ecount(num_decls) Z3_func_decl const decls[] + void Z3_API Z3_parse_smtlib_string(Z3_context c, + Z3_string str, + unsigned num_sorts, + Z3_symbol const sort_names[], + Z3_sort const sorts[], + unsigned num_decls, + Z3_symbol const decl_names[], + Z3_func_decl const decls[] ); /** @@ -5098,14 +5172,14 @@ END_MLAPI_EXCLUDE def_API('Z3_parse_smtlib_file', VOID, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ - void Z3_API Z3_parse_smtlib_file(__in Z3_context c, - __in Z3_string file_name, - __in unsigned num_sorts, - __in_ecount(num_sorts) Z3_symbol const sort_names[], - __in_ecount(num_sorts) Z3_sort const sorts[], - __in unsigned num_decls, - __in_ecount(num_decls) Z3_symbol const decl_names[], - __in_ecount(num_decls) Z3_func_decl const decls[] + void Z3_API Z3_parse_smtlib_file(Z3_context c, + Z3_string file_name, + unsigned num_sorts, + Z3_symbol const sort_names[], + Z3_sort const sorts[], + unsigned num_decls, + Z3_symbol const decl_names[], + Z3_func_decl const decls[] ); /** @@ -5113,7 +5187,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_smtlib_num_formulas', UINT, (_in(CONTEXT), )) */ - unsigned Z3_API Z3_get_smtlib_num_formulas(__in Z3_context c); + unsigned Z3_API Z3_get_smtlib_num_formulas(Z3_context c); /** \brief \mlh get_smtlib_formula c i \endmlh @@ -5123,14 +5197,14 @@ END_MLAPI_EXCLUDE def_API('Z3_get_smtlib_formula', AST, (_in(CONTEXT), _in(UINT))) */ - Z3_ast Z3_API Z3_get_smtlib_formula(__in Z3_context c, __in unsigned i); + Z3_ast Z3_API Z3_get_smtlib_formula(Z3_context c, unsigned i); /** \brief Return the number of SMTLIB assumptions parsed by #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. def_API('Z3_get_smtlib_num_assumptions', UINT, (_in(CONTEXT), )) */ - unsigned Z3_API Z3_get_smtlib_num_assumptions(__in Z3_context c); + unsigned Z3_API Z3_get_smtlib_num_assumptions(Z3_context c); /** \brief \mlh get_smtlib_assumption c i \endmlh @@ -5140,14 +5214,14 @@ END_MLAPI_EXCLUDE def_API('Z3_get_smtlib_assumption', AST, (_in(CONTEXT), _in(UINT))) */ - Z3_ast Z3_API Z3_get_smtlib_assumption(__in Z3_context c, __in unsigned i); + Z3_ast Z3_API Z3_get_smtlib_assumption(Z3_context c, unsigned i); /** \brief Return the number of declarations parsed by #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. def_API('Z3_get_smtlib_num_decls', UINT, (_in(CONTEXT), )) */ - unsigned Z3_API Z3_get_smtlib_num_decls(__in Z3_context c); + unsigned Z3_API Z3_get_smtlib_num_decls(Z3_context c); /** \brief \mlh get_smtlib_decl c i \endmlh @@ -5157,14 +5231,14 @@ END_MLAPI_EXCLUDE def_API('Z3_get_smtlib_decl', FUNC_DECL, (_in(CONTEXT), _in(UINT))) */ - Z3_func_decl Z3_API Z3_get_smtlib_decl(__in Z3_context c, __in unsigned i); + Z3_func_decl Z3_API Z3_get_smtlib_decl(Z3_context c, unsigned i); /** \brief Return the number of sorts parsed by #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. def_API('Z3_get_smtlib_num_sorts', UINT, (_in(CONTEXT), )) */ - unsigned Z3_API Z3_get_smtlib_num_sorts(__in Z3_context c); + unsigned Z3_API Z3_get_smtlib_num_sorts(Z3_context c); /** \brief \mlh get_smtlib_sort c i \endmlh @@ -5174,7 +5248,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_smtlib_sort', SORT, (_in(CONTEXT), _in(UINT))) */ - Z3_sort Z3_API Z3_get_smtlib_sort(__in Z3_context c, __in unsigned i); + Z3_sort Z3_API Z3_get_smtlib_sort(Z3_context c, unsigned i); BEGIN_MLAPI_EXCLUDE /** @@ -5183,7 +5257,7 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_get_smtlib_error', STRING, (_in(CONTEXT), )) */ - Z3_string Z3_API Z3_get_smtlib_error(__in Z3_context c); + Z3_string Z3_API Z3_get_smtlib_error(Z3_context c); END_MLAPI_EXCLUDE /*@}*/ @@ -5205,7 +5279,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_error_code', UINT, (_in(CONTEXT), )) */ - Z3_error_code Z3_API Z3_get_error_code(__in Z3_context c); + Z3_error_code Z3_API Z3_get_error_code(Z3_context c); /** \brief Register a Z3 error handler. @@ -5219,7 +5293,7 @@ END_MLAPI_EXCLUDE \sa Z3_get_error_code */ - void Z3_API Z3_set_error_handler(__in Z3_context c, __in Z3_error_handler h); + void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h); #endif /** @@ -5227,7 +5301,7 @@ END_MLAPI_EXCLUDE def_API('Z3_set_error', VOID, (_in(CONTEXT), _in(ERROR_CODE))) */ - void Z3_API Z3_set_error(__in Z3_context c, __in Z3_error_code e); + void Z3_API Z3_set_error(Z3_context c, Z3_error_code e); #ifdef Conly /** @@ -5237,7 +5311,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_error_msg', STRING, (_in(ERROR_CODE),)) */ - Z3_string Z3_API Z3_get_error_msg(__in Z3_error_code err); + Z3_string Z3_API Z3_get_error_msg(Z3_error_code err); #endif BEGIN_MLAPI_EXCLUDE @@ -5246,7 +5320,7 @@ BEGIN_MLAPI_EXCLUDE def_API('Z3_get_error_msg_ex', STRING, (_in(CONTEXT), _in(ERROR_CODE))) */ - Z3_string Z3_API Z3_get_error_msg_ex(__in Z3_context c, __in Z3_error_code err); + Z3_string Z3_API Z3_get_error_msg_ex(Z3_context c, Z3_error_code err); END_MLAPI_EXCLUDE #ifdef ML4only #include @@ -5266,7 +5340,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_version', VOID, (_out(UINT), _out(UINT), _out(UINT), _out(UINT))) */ - void Z3_API Z3_get_version(__out unsigned * major, __out unsigned * minor, __out unsigned * build_number, __out unsigned * revision_number); + void Z3_API Z3_get_version(unsigned * major, unsigned * minor, unsigned * build_number, unsigned * revision_number); /** \brief Enable tracing messages tagged as \c tag when Z3 is compiled in debug mode. @@ -5274,7 +5348,7 @@ END_MLAPI_EXCLUDE def_API('Z3_enable_trace', VOID, (_in(STRING),)) */ - void Z3_API Z3_enable_trace(__in Z3_string tag); + void Z3_API Z3_enable_trace(Z3_string tag); /** \brief Disable tracing messages tagged as \c tag when Z3 is compiled in debug mode. @@ -5282,7 +5356,7 @@ END_MLAPI_EXCLUDE def_API('Z3_disable_trace', VOID, (_in(STRING),)) */ - void Z3_API Z3_disable_trace(__in Z3_string tag); + void Z3_API Z3_disable_trace(Z3_string tag); #ifdef CorML3 /** @@ -5297,7 +5371,19 @@ END_MLAPI_EXCLUDE */ void Z3_API Z3_reset_memory(void); #endif - + +#ifdef CorML3 + /** + \brief Destroy all allocated resources. + + Any pointers previously returned by the API become invalid. + Can be used for memory leak detection. + + def_API('Z3_finalize_memory', VOID, ()) + */ + void Z3_API Z3_finalize_memory(void); +#endif + /*@}*/ #ifdef CorML3 @@ -5311,21 +5397,21 @@ END_MLAPI_EXCLUDE // // callbacks and void* don't work with CAMLIDL. // - typedef Z3_bool Z3_reduce_eq_callback_fptr(__in Z3_theory t, __in Z3_ast a, __in Z3_ast b, __out Z3_ast * r); + typedef Z3_bool Z3_reduce_eq_callback_fptr(Z3_theory t, Z3_ast a, Z3_ast b, Z3_ast * r); - typedef Z3_bool Z3_reduce_app_callback_fptr(__in Z3_theory, __in Z3_func_decl, __in unsigned, __in Z3_ast const [], __out Z3_ast *); + typedef Z3_bool Z3_reduce_app_callback_fptr(Z3_theory, Z3_func_decl, unsigned, Z3_ast const [], Z3_ast *); - typedef Z3_bool Z3_reduce_distinct_callback_fptr(__in Z3_theory, __in unsigned, __in Z3_ast const [], __out Z3_ast *); + typedef Z3_bool Z3_reduce_distinct_callback_fptr(Z3_theory, unsigned, Z3_ast const [], Z3_ast *); - typedef void Z3_theory_callback_fptr(__in Z3_theory t); + typedef void Z3_theory_callback_fptr(Z3_theory t); - typedef Z3_bool Z3_theory_final_check_callback_fptr(__in Z3_theory); + typedef Z3_bool Z3_theory_final_check_callback_fptr(Z3_theory); - typedef void Z3_theory_ast_callback_fptr(__in Z3_theory, __in Z3_ast); + typedef void Z3_theory_ast_callback_fptr(Z3_theory, Z3_ast); - typedef void Z3_theory_ast_bool_callback_fptr(__in Z3_theory, __in Z3_ast, __in Z3_bool); + typedef void Z3_theory_ast_bool_callback_fptr(Z3_theory, Z3_ast, Z3_bool); - typedef void Z3_theory_ast_ast_callback_fptr(__in Z3_theory, __in Z3_ast, __in Z3_ast); + typedef void Z3_theory_ast_ast_callback_fptr(Z3_theory, Z3_ast, Z3_ast); #endif @@ -5338,42 +5424,42 @@ END_MLAPI_EXCLUDE \conly \c data is a pointer to an external data-structure that may be used to store \conly theory specific additional data. */ - Z3_theory Z3_API Z3_mk_theory(__in Z3_context c, __in Z3_string th_name, __in Z3_theory_data data); + Z3_theory Z3_API Z3_mk_theory(Z3_context c, Z3_string th_name, Z3_theory_data data); /** \brief Return a pointer to the external data-structure supplied to the function #Z3_mk_theory. \see Z3_mk_theory */ - Z3_theory_data Z3_API Z3_theory_get_ext_data(__in Z3_theory t); + Z3_theory_data Z3_API Z3_theory_get_ext_data(Z3_theory t); #endif /** \brief Create an interpreted theory sort. */ - Z3_sort Z3_API Z3_theory_mk_sort(__in Z3_context c, __in Z3_theory t, __in Z3_symbol s); + Z3_sort Z3_API Z3_theory_mk_sort(Z3_context c, Z3_theory t, Z3_symbol s); /** \brief Create an interpreted theory constant value. Values are assumed to be different from each other. */ - Z3_ast Z3_API Z3_theory_mk_value(__in Z3_context c, __in Z3_theory t, __in Z3_symbol n, __in Z3_sort s); + Z3_ast Z3_API Z3_theory_mk_value(Z3_context c, Z3_theory t, Z3_symbol n, Z3_sort s); /** \brief Create an interpreted constant for the given theory. */ - Z3_ast Z3_API Z3_theory_mk_constant(__in Z3_context c, __in Z3_theory t, __in Z3_symbol n, __in Z3_sort s); + Z3_ast Z3_API Z3_theory_mk_constant(Z3_context c, Z3_theory t, Z3_symbol n, Z3_sort s); /** \brief Create an interpreted function declaration for the given theory. */ - Z3_func_decl Z3_API Z3_theory_mk_func_decl(__in Z3_context c, __in Z3_theory t, __in Z3_symbol n, - __in unsigned domain_size, __in_ecount(domain_size) Z3_sort const domain[], - __in Z3_sort range); + Z3_func_decl Z3_API Z3_theory_mk_func_decl(Z3_context c, Z3_theory t, Z3_symbol n, + unsigned domain_size, Z3_sort const domain[], + Z3_sort range); /** \brief Return the context where the given theory is installed. */ - Z3_context Z3_API Z3_theory_get_context(__in Z3_theory t); + Z3_context Z3_API Z3_theory_get_context(Z3_theory t); #ifdef Conly @@ -5387,7 +5473,7 @@ END_MLAPI_EXCLUDE \see Z3_mk_theory \conly \see Z3_theory_get_ext_data */ - void Z3_API Z3_set_delete_callback(__in Z3_theory t, __in Z3_theory_callback_fptr f); + void Z3_API Z3_set_delete_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback for simplifying operators of the given theory. @@ -5402,7 +5488,7 @@ END_MLAPI_EXCLUDE \conly If f(t, d, n, args, r) returns false, then \c r is ignored, and Z3 assumes that no simplification was performed. */ - void Z3_API Z3_set_reduce_app_callback(__in Z3_theory t, __in Z3_reduce_app_callback_fptr f); + void Z3_API Z3_set_reduce_app_callback(Z3_theory t, Z3_reduce_app_callback_fptr f); /** \brief Set a callback for simplifying the atom s_1 = s_2, when the @@ -5417,7 +5503,7 @@ END_MLAPI_EXCLUDE \conly If f(t, s_1, s_2, r) returns false, then \c r is ignored, and Z3 assumes that no simplification was performed. */ - void Z3_API Z3_set_reduce_eq_callback(__in Z3_theory t, __in Z3_reduce_eq_callback_fptr f); + void Z3_API Z3_set_reduce_eq_callback(Z3_theory t, Z3_reduce_eq_callback_fptr f); /** \brief Set a callback for simplifying the atom distinct(s_1, ..., s_n), when the @@ -5432,7 +5518,7 @@ END_MLAPI_EXCLUDE \conly If f(t, n, args, r) returns false, then \c r is ignored, and Z3 assumes that no simplification was performed. */ - void Z3_API Z3_set_reduce_distinct_callback(__in Z3_theory t, __in Z3_reduce_distinct_callback_fptr f); + void Z3_API Z3_set_reduce_distinct_callback(Z3_theory t, Z3_reduce_distinct_callback_fptr f); /** \brief Set a callback that is invoked when a theory application @@ -5449,7 +5535,7 @@ END_MLAPI_EXCLUDE \remark An expression \c n added to the logical context at search level \c n, will remain in the logical context until this level is backtracked. */ - void Z3_API Z3_set_new_app_callback(__in Z3_theory t, __in Z3_theory_ast_callback_fptr f); + void Z3_API Z3_set_new_app_callback(Z3_theory t, Z3_theory_ast_callback_fptr f); /** \brief Set a callback that is invoked when an expression of @@ -5467,7 +5553,7 @@ END_MLAPI_EXCLUDE \remark An expression \c n added to the logical context at search level \c n, will remain in the logical context until this level is backtracked. */ - void Z3_API Z3_set_new_elem_callback(__in Z3_theory t, __in Z3_theory_ast_callback_fptr f); + void Z3_API Z3_set_new_elem_callback(Z3_theory t, Z3_theory_ast_callback_fptr f); /** \brief Set a callback that is invoked when Z3 starts searching for a @@ -5476,7 +5562,7 @@ END_MLAPI_EXCLUDE \conly The callback has the form f(t), where \conly - \c t is the given theory */ - void Z3_API Z3_set_init_search_callback(__in Z3_theory t, __in Z3_theory_callback_fptr f); + void Z3_API Z3_set_init_search_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked when Z3 creates a @@ -5487,7 +5573,7 @@ END_MLAPI_EXCLUDE \conly The callback has the form f(t), where \conly - \c t is the given theory */ - void Z3_API Z3_set_push_callback(__in Z3_theory t, __in Z3_theory_callback_fptr f); + void Z3_API Z3_set_push_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked when Z3 backtracks a @@ -5498,7 +5584,7 @@ END_MLAPI_EXCLUDE \conly The callback has the form f(t), where \conly - \c t is the given theory */ - void Z3_API Z3_set_pop_callback(__in Z3_theory t, __in Z3_theory_callback_fptr f); + void Z3_API Z3_set_pop_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked when Z3 restarts the @@ -5507,7 +5593,7 @@ END_MLAPI_EXCLUDE \conly The callback has the form f(t), where \conly - \c t is the given theory */ - void Z3_API Z3_set_restart_callback(__in Z3_theory t, __in Z3_theory_callback_fptr f); + void Z3_API Z3_set_restart_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked when the logical context @@ -5517,7 +5603,7 @@ END_MLAPI_EXCLUDE \conly The callback has the form f(t), where \conly - \c t is the given theory */ - void Z3_API Z3_set_reset_callback(__in Z3_theory t, __in Z3_theory_callback_fptr f); + void Z3_API Z3_set_reset_callback(Z3_theory t, Z3_theory_callback_fptr f); /** \brief Set a callback that is invoked before Z3 starts building a model. @@ -5531,7 +5617,7 @@ END_MLAPI_EXCLUDE and it will assume that it was not possible to decide if the asserted constraints are satisfiable or not. */ - void Z3_API Z3_set_final_check_callback(__in Z3_theory t, __in Z3_theory_final_check_callback_fptr f); + void Z3_API Z3_set_final_check_callback(Z3_theory t, Z3_theory_final_check_callback_fptr f); /** \brief Set a callback that is invoked when an equality s_1 = s_2 @@ -5542,7 +5628,7 @@ END_MLAPI_EXCLUDE \conly - \c s_1 is the left-hand-side \conly - \c s_2 is the right-hand-side */ - void Z3_API Z3_set_new_eq_callback(__in Z3_theory t, __in Z3_theory_ast_ast_callback_fptr f); + void Z3_API Z3_set_new_eq_callback(Z3_theory t, Z3_theory_ast_ast_callback_fptr f); /** \brief Set a callback that is invoked when a disequality s_1 != s_2 @@ -5553,7 +5639,7 @@ END_MLAPI_EXCLUDE \conly - \c s_1 is the left-hand-side \conly - \c s_2 is the right-hand-side */ - void Z3_API Z3_set_new_diseq_callback(__in Z3_theory t, __in Z3_theory_ast_ast_callback_fptr f); + void Z3_API Z3_set_new_diseq_callback(Z3_theory t, Z3_theory_ast_ast_callback_fptr f); /** \brief Set a callback that is invoked when a theory predicate is assigned to true/false by Z3. @@ -5563,7 +5649,7 @@ END_MLAPI_EXCLUDE \conly - \c p is the assigned predicate. \conly - \c v is the value (true/false) assigned to \c p. */ - void Z3_API Z3_set_new_assignment_callback(__in Z3_theory t, __in Z3_theory_ast_bool_callback_fptr f); + void Z3_API Z3_set_new_assignment_callback(Z3_theory t, Z3_theory_ast_bool_callback_fptr f); /** \brief Set a callback that is invoked when an expression is @@ -5574,7 +5660,7 @@ END_MLAPI_EXCLUDE \conly - \c t is the given theory \conly - \c n is the relevant expression */ - void Z3_API Z3_set_new_relevant_callback(__in Z3_theory t, __in Z3_theory_ast_callback_fptr f); + void Z3_API Z3_set_new_relevant_callback(Z3_theory t, Z3_theory_ast_callback_fptr f); #endif @@ -5592,7 +5678,7 @@ END_MLAPI_EXCLUDE Z3 tracks the theory axioms asserted. So, multiple assertions of the same axiom are ignored. */ - void Z3_API Z3_theory_assert_axiom(__in Z3_theory t, __in Z3_ast ax); + void Z3_API Z3_theory_assert_axiom(Z3_theory t, Z3_ast ax); /** \brief Inform to the logical context that \c lhs and \c rhs have the same interpretation @@ -5601,7 +5687,7 @@ END_MLAPI_EXCLUDE For more information, see the paper "Model-Based Theory Combination" in the Z3 website. */ - void Z3_API Z3_theory_assume_eq(__in Z3_theory t, __in Z3_ast lhs, __in Z3_ast rhs); + void Z3_API Z3_theory_assume_eq(Z3_theory t, Z3_ast lhs, Z3_ast rhs); /** \brief Enable/disable the simplification of theory axioms asserted using #Z3_theory_assert_axiom. @@ -5609,12 +5695,12 @@ END_MLAPI_EXCLUDE That is, the reduce theory callbacks are not invoked for theory axioms. The default behavior is useful when asserting axioms stating properties of theory operators. */ - void Z3_API Z3_theory_enable_axiom_simplification(__in Z3_theory t, __in Z3_bool flag); + void Z3_API Z3_theory_enable_axiom_simplification(Z3_theory t, Z3_bool flag); /** \brief Return the root of the equivalence class containing \c n. */ - Z3_ast Z3_API Z3_theory_get_eqc_root(__in Z3_theory t, __in Z3_ast n); + Z3_ast Z3_API Z3_theory_get_eqc_root(Z3_theory t, Z3_ast n); /** \brief Return the next element in the equivalence class containing \c n. @@ -5630,56 +5716,56 @@ END_MLAPI_EXCLUDE while (curr != n); \endcode */ - Z3_ast Z3_API Z3_theory_get_eqc_next(__in Z3_theory t, __in Z3_ast n); + Z3_ast Z3_API Z3_theory_get_eqc_next(Z3_theory t, Z3_ast n); /** \brief Return the number of parents of \c n that are operators of the given theory. */ - unsigned Z3_API Z3_theory_get_num_parents(__in Z3_theory t, __in Z3_ast n); + unsigned Z3_API Z3_theory_get_num_parents(Z3_theory t, Z3_ast n); /** \brief Return the i-th parent of \c n. See #Z3_theory_get_num_parents. */ - Z3_ast Z3_API Z3_theory_get_parent(__in Z3_theory t, __in Z3_ast n, __in unsigned i); + Z3_ast Z3_API Z3_theory_get_parent(Z3_theory t, Z3_ast n, unsigned i); /** \brief Return \c Z3_TRUE if \c n is an interpreted theory value. */ - Z3_bool Z3_API Z3_theory_is_value(__in Z3_theory t, __in Z3_ast n); + Z3_bool Z3_API Z3_theory_is_value(Z3_theory t, Z3_ast n); /** \brief Return \c Z3_TRUE if \c d is an interpreted theory declaration. */ - Z3_bool Z3_API Z3_theory_is_decl(__in Z3_theory t, __in Z3_func_decl d); + Z3_bool Z3_API Z3_theory_is_decl(Z3_theory t, Z3_func_decl d); /** \brief Return the number of expressions of the given theory in the logical context. These are the expressions notified using the callback #Z3_set_new_elem_callback. */ - unsigned Z3_API Z3_theory_get_num_elems(__in Z3_theory t); + unsigned Z3_API Z3_theory_get_num_elems(Z3_theory t); /** \brief Return the i-th elem of the given theory in the logical context. \see Z3_theory_get_num_elems */ - Z3_ast Z3_API Z3_theory_get_elem(__in Z3_theory t, __in unsigned i); + Z3_ast Z3_API Z3_theory_get_elem(Z3_theory t, unsigned i); /** \brief Return the number of theory applications in the logical context. These are the expressions notified using the callback #Z3_set_new_app_callback. */ - unsigned Z3_API Z3_theory_get_num_apps(__in Z3_theory t); + unsigned Z3_API Z3_theory_get_num_apps(Z3_theory t); /** \brief Return the i-th application of the given theory in the logical context. \see Z3_theory_get_num_apps */ - Z3_ast Z3_API Z3_theory_get_app(__in Z3_theory t, __in unsigned i); + Z3_ast Z3_API Z3_theory_get_app(Z3_theory t, unsigned i); /*@}*/ @@ -5699,7 +5785,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_fixedpoint', FIXEDPOINT, (_in(CONTEXT), )) */ - Z3_fixedpoint Z3_API Z3_mk_fixedpoint(__in Z3_context c); + Z3_fixedpoint Z3_API Z3_mk_fixedpoint(Z3_context c); #ifdef Conly /** @@ -5707,14 +5793,14 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_inc_ref', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) */ - void Z3_API Z3_fixedpoint_inc_ref(__in Z3_context c,__in Z3_fixedpoint d); + void Z3_API Z3_fixedpoint_inc_ref(Z3_context c,Z3_fixedpoint d); /** \brief Decrement the reference counter of the given fixedpoint context. def_API('Z3_fixedpoint_dec_ref', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) */ - void Z3_API Z3_fixedpoint_dec_ref(__in Z3_context c,__in Z3_fixedpoint d); + void Z3_API Z3_fixedpoint_dec_ref(Z3_context c,Z3_fixedpoint d); #endif /** @@ -5729,7 +5815,7 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_add_rule', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(SYMBOL))) */ - void Z3_API Z3_fixedpoint_add_rule(__in Z3_context c,__in Z3_fixedpoint d, __in Z3_ast rule, __in Z3_symbol name); + void Z3_API Z3_fixedpoint_add_rule(Z3_context c,Z3_fixedpoint d, Z3_ast rule, Z3_symbol name); /** \brief Add a Database fact. @@ -5748,9 +5834,9 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_add_fact', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(UINT), _in_array(3, UINT))) */ - void Z3_API Z3_fixedpoint_add_fact(__in Z3_context c,__in Z3_fixedpoint d, - __in Z3_func_decl r, - __in unsigned num_args, __in_ecount(num_args) unsigned args[]); + void Z3_API Z3_fixedpoint_add_fact(Z3_context c,Z3_fixedpoint d, + Z3_func_decl r, + unsigned num_args, unsigned args[]); /** \brief Assert a constraint to the fixedpoint context. @@ -5760,7 +5846,7 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_assert', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST))) */ - void Z3_API Z3_fixedpoint_assert(__in Z3_context c,__in Z3_fixedpoint d, __in Z3_ast axiom); + void Z3_API Z3_fixedpoint_assert(Z3_context c,Z3_fixedpoint d, Z3_ast axiom); /** \brief Pose a query against the asserted rules. @@ -5777,7 +5863,7 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_query', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST))) */ - Z3_lbool Z3_API Z3_fixedpoint_query(__in Z3_context c,__in Z3_fixedpoint d, __in Z3_ast query); + Z3_lbool Z3_API Z3_fixedpoint_query(Z3_context c,Z3_fixedpoint d, Z3_ast query); /** \brief Pose multiple queries against the asserted rules. @@ -5792,8 +5878,8 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_query_relations', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, FUNC_DECL))) */ Z3_lbool Z3_API Z3_fixedpoint_query_relations( - __in Z3_context c,__in Z3_fixedpoint d, - __in unsigned num_relations, __in_ecount(num_relations) Z3_func_decl const relations[]); + Z3_context c,Z3_fixedpoint d, + unsigned num_relations, Z3_func_decl const relations[]); /** \brief Retrieve a formula that encodes satisfying answers to the query. @@ -5808,7 +5894,7 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_get_answer', AST, (_in(CONTEXT), _in(FIXEDPOINT))) */ - Z3_ast Z3_API Z3_fixedpoint_get_answer(__in Z3_context c,__in Z3_fixedpoint d); + Z3_ast Z3_API Z3_fixedpoint_get_answer(Z3_context c,Z3_fixedpoint d); /** \brief Retrieve a string that describes the last status returned by #Z3_fixedpoint_query. @@ -5817,7 +5903,7 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_get_reason_unknown', STRING, (_in(CONTEXT), _in(FIXEDPOINT) )) */ - Z3_string Z3_API Z3_fixedpoint_get_reason_unknown(__in Z3_context c,__in Z3_fixedpoint d); + Z3_string Z3_API Z3_fixedpoint_get_reason_unknown(Z3_context c,Z3_fixedpoint d); /** \brief Update a named rule. @@ -5825,7 +5911,7 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_update_rule', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(SYMBOL))) */ - void Z3_API Z3_fixedpoint_update_rule(__in Z3_context c, __in Z3_fixedpoint d, __in Z3_ast a, __in Z3_symbol name); + void Z3_API Z3_fixedpoint_update_rule(Z3_context c, Z3_fixedpoint d, Z3_ast a, Z3_symbol name); /** \brief Query the PDR engine for the maximal levels properties are known about predicate. @@ -5869,7 +5955,7 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_get_statistics', STATS, (_in(CONTEXT), _in(FIXEDPOINT))) */ - Z3_stats Z3_API Z3_fixedpoint_get_statistics(__in Z3_context c,__in Z3_fixedpoint d); + Z3_stats Z3_API Z3_fixedpoint_get_statistics(Z3_context c,Z3_fixedpoint d); /** \brief Register relation as Fixedpoint defined. @@ -5879,7 +5965,7 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_register_relation', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) */ - void Z3_API Z3_fixedpoint_register_relation(__in Z3_context c,__in Z3_fixedpoint d, __in Z3_func_decl f); + void Z3_API Z3_fixedpoint_register_relation(Z3_context c,Z3_fixedpoint d, Z3_func_decl f); /** \brief Configure the predicate representation. @@ -5891,11 +5977,11 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_set_predicate_representation', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(UINT), _in_array(3, SYMBOL))) */ void Z3_API Z3_fixedpoint_set_predicate_representation( - __in Z3_context c, - __in Z3_fixedpoint d, - __in Z3_func_decl f, - __in unsigned num_relations, - __in_ecount(num_relations) Z3_symbol const relation_kinds[]); + Z3_context c, + Z3_fixedpoint d, + Z3_func_decl f, + unsigned num_relations, + Z3_symbol const relation_kinds[]); /** \brief Retrieve set of rules from fixedpoint context. @@ -5903,8 +5989,8 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_get_rules', AST_VECTOR, (_in(CONTEXT),_in(FIXEDPOINT))) */ Z3_ast_vector Z3_API Z3_fixedpoint_get_rules( - __in Z3_context c, - __in Z3_fixedpoint f); + Z3_context c, + Z3_fixedpoint f); /** \brief Retrieve set of background assertions from fixedpoint context. @@ -5912,29 +5998,29 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_get_assertions', AST_VECTOR, (_in(CONTEXT),_in(FIXEDPOINT))) */ Z3_ast_vector Z3_API Z3_fixedpoint_get_assertions( - __in Z3_context c, - __in Z3_fixedpoint f); + Z3_context c, + Z3_fixedpoint f); /** \brief Set parameters on fixedpoint context. def_API('Z3_fixedpoint_set_params', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(PARAMS))) */ - void Z3_API Z3_fixedpoint_set_params(__in Z3_context c, __in Z3_fixedpoint f, __in Z3_params p); + void Z3_API Z3_fixedpoint_set_params(Z3_context c, Z3_fixedpoint f, Z3_params p); /** \brief Return a string describing all fixedpoint available parameters. def_API('Z3_fixedpoint_get_help', STRING, (_in(CONTEXT), _in(FIXEDPOINT))) */ - Z3_string Z3_API Z3_fixedpoint_get_help(__in Z3_context c, __in Z3_fixedpoint f); + Z3_string Z3_API Z3_fixedpoint_get_help(Z3_context c, Z3_fixedpoint f); /** \brief Return the parameter description set for the given fixedpoint object. def_API('Z3_fixedpoint_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(FIXEDPOINT))) */ - Z3_param_descrs Z3_API Z3_fixedpoint_get_param_descrs(__in Z3_context c, __in Z3_fixedpoint f); + Z3_param_descrs Z3_API Z3_fixedpoint_get_param_descrs(Z3_context c, Z3_fixedpoint f); /** \brief Print the current rules and background axioms as a string. @@ -5946,15 +6032,15 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_to_string', STRING, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, AST))) */ Z3_string Z3_API Z3_fixedpoint_to_string( - __in Z3_context c, - __in Z3_fixedpoint f, - __in unsigned num_queries, - __in_ecount(num_queries) Z3_ast queries[]); + Z3_context c, + Z3_fixedpoint f, + unsigned num_queries, + Z3_ast queries[]); /** \brief Parse an SMT-LIB2 string with fixedpoint rules. Add the rules to the current fixedpoint context. - Return the set of queries in the file. + Return the set of queries in the string. \param c - context. \param f - fixedpoint context. @@ -5963,9 +6049,9 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_from_string', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT), _in(STRING))) */ Z3_ast_vector Z3_API Z3_fixedpoint_from_string( - __in Z3_context c, - __in Z3_fixedpoint f, - __in Z3_string s); + Z3_context c, + Z3_fixedpoint f, + Z3_string s); /** \brief Parse an SMT-LIB2 file with fixedpoint rules. @@ -5979,9 +6065,9 @@ END_MLAPI_EXCLUDE def_API('Z3_fixedpoint_from_file', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT), _in(STRING))) */ Z3_ast_vector Z3_API Z3_fixedpoint_from_file( - __in Z3_context c, - __in Z3_fixedpoint f, - __in Z3_string s); + Z3_context c, + Z3_fixedpoint f, + Z3_string s); /** \brief Create a backtracking point. @@ -6013,20 +6099,20 @@ END_MLAPI_EXCLUDE */ typedef void Z3_fixedpoint_reduce_assign_callback_fptr( - __in void*, __in Z3_func_decl, - __in unsigned, __in Z3_ast const [], - __in unsigned, __in Z3_ast const []); + void*, Z3_func_decl, + unsigned, Z3_ast const [], + unsigned, Z3_ast const []); typedef void Z3_fixedpoint_reduce_app_callback_fptr( - __in void*, __in Z3_func_decl, - __in unsigned, __in Z3_ast const [], - __out Z3_ast*); + void*, Z3_func_decl, + unsigned, Z3_ast const [], + Z3_ast*); /** \brief Initialize the context with a user-defined state. */ - void Z3_API Z3_fixedpoint_init(__in Z3_context c,__in Z3_fixedpoint d, __in void* state); + void Z3_API Z3_fixedpoint_init(Z3_context c,Z3_fixedpoint d, void* state); /** \brief Register a callback to destructive updates. @@ -6034,18 +6120,218 @@ END_MLAPI_EXCLUDE Registers are identified with terms encoded as fresh constants, */ void Z3_API Z3_fixedpoint_set_reduce_assign_callback( - __in Z3_context c,__in Z3_fixedpoint d, __in Z3_fixedpoint_reduce_assign_callback_fptr cb); + Z3_context c,Z3_fixedpoint d, Z3_fixedpoint_reduce_assign_callback_fptr cb); /** \brief Register a callback for buildling terms based on the relational operators. */ void Z3_API Z3_fixedpoint_set_reduce_app_callback( - __in Z3_context c,__in Z3_fixedpoint d, __in Z3_fixedpoint_reduce_app_callback_fptr cb); + Z3_context c,Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr cb); #endif #endif + + +#ifdef CorML4 + /** + @name Optimize facilities + */ + /*@{*/ + + /** + \brief Create a new optimize context. + + \conly \remark User must use #Z3_optimize_inc_ref and #Z3_optimize_dec_ref to manage optimize objects. + \conly Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. + + def_API('Z3_mk_optimize', OPTIMIZE, (_in(CONTEXT), )) + */ + Z3_optimize Z3_API Z3_mk_optimize(Z3_context c); + +#ifdef Conly + /** + \brief Increment the reference counter of the given optimize context + + def_API('Z3_optimize_inc_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE))) + */ + void Z3_API Z3_optimize_inc_ref(Z3_context c,Z3_optimize d); + + /** + \brief Decrement the reference counter of the given optimize context. + + def_API('Z3_optimize_dec_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE))) + */ + void Z3_API Z3_optimize_dec_ref(Z3_context c,Z3_optimize d); +#endif + + /** + \brief Assert hard constraint to the optimization context. + + def_API('Z3_optimize_assert', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) + */ + void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a); + + + /** + \brief Assert soft constraint to the optimization context. + \param c - context + \param o - optimization context + \param a - formula + \param weight - a positive weight, penalty for violating soft constraint + \param id - optional identifier to group soft constraints + + def_API('Z3_optimize_assert_soft', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(STRING), _in(SYMBOL))) + */ + unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id); + + + /** + \brief Add a maximization constraint. + \param c - context + \param o - optimization context + \param a - arithmetical term + def_API('Z3_optimize_maximize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) + */ + unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t); + + /** + \brief Add a minimization constraint. + \param c - context + \param o - optimization context + \param a - arithmetical term + + def_API('Z3_optimize_minimize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) + */ + unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t); + + + /** + \brief Create a backtracking point. + + The optimize solver contains a set of rules, added facts and assertions. + The set of rules, facts and assertions are restored upon calling #Z3_optimize_pop. + + \sa Z3_optimize_pop + + def_API('Z3_optimize_push', VOID, (_in(CONTEXT), _in(OPTIMIZE))) + */ + void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d); + + /** + \brief Backtrack one level. + + \sa Z3_optimize_push + + \pre The number of calls to pop cannot exceed calls to push. + + def_API('Z3_optimize_pop', VOID, (_in(CONTEXT), _in(OPTIMIZE))) + */ + void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d); + + /** + \brief Check consistency and produce optimal values. + \param c - context + \param o - optimization context + + def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE))) + */ + Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o); + + + /** + \brief Retrieve a string that describes the last status returned by #Z3_optimize_check. + + Use this method when #Z3_optimize_check returns Z3_L_UNDEF. + + def_API('Z3_optimize_get_reason_unknown', STRING, (_in(CONTEXT), _in(OPTIMIZE) )) + */ + Z3_string Z3_API Z3_optimize_get_reason_unknown(Z3_context c,Z3_optimize d); + + /** + \brief Retrieve the model for the last #Z3_optimize_check + + The error handler is invoked if a model is not available because + the commands above were not invoked for the given optimization + solver, or if the result was \c Z3_L_FALSE. + + def_API('Z3_optimize_get_model', MODEL, (_in(CONTEXT), _in(OPTIMIZE))) + */ + Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o); + + /** + \brief Set parameters on optimization context. + + \param c - context + \param o - optimization context + \param p - parameters + + def_API('Z3_optimize_set_params', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(PARAMS))) + */ + void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p); + + /** + \brief Return the parameter description set for the given optimize object. + + \param c - context + \param o - optimization context + + def_API('Z3_optimize_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(OPTIMIZE))) + */ + Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o); + + /** + \brief Retrieve lower bound value or approximation for the i'th optimization objective. + + \param c - context + \param o - optimization context + \param idx - index of optimization objective + + def_API('Z3_optimize_get_lower', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) + */ + Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx); + + /** + \brief Retrieve upper bound value or approximation for the i'th optimization objective. + + \param c - context + \param o - optimization context + \param idx - index of optimization objective + + def_API('Z3_optimize_get_upper', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) + */ + Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx); + + /** + \brief Print the current context as a string. + \param c - context. + \param o - optimization context. + + def_API('Z3_optimize_to_string', STRING, (_in(CONTEXT), _in(OPTIMIZE))) + */ + Z3_string Z3_API Z3_optimize_to_string( + Z3_context c, + Z3_optimize o); + + + /** + \brief Return a string containing a description of parameters accepted by optimize. + + def_API('Z3_optimize_get_help', STRING, (_in(CONTEXT), _in(OPTIMIZE))) + */ + Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize t); + + /** + \brief Retrieve statistics information from the last call to #Z3_optimize_check + + def_API('Z3_optimize_get_statistics', STATS, (_in(CONTEXT), _in(OPTIMIZE))) + */ + Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c,Z3_optimize d); + + +#endif + #ifdef CorML4 /*@}*/ @@ -6062,7 +6348,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_ast_vector', AST_VECTOR, (_in(CONTEXT),)) */ - Z3_ast_vector Z3_API Z3_mk_ast_vector(__in Z3_context c); + Z3_ast_vector Z3_API Z3_mk_ast_vector(Z3_context c); #ifdef Conly /** @@ -6070,14 +6356,14 @@ END_MLAPI_EXCLUDE def_API('Z3_ast_vector_inc_ref', VOID, (_in(CONTEXT), _in(AST_VECTOR))) */ - void Z3_API Z3_ast_vector_inc_ref(__in Z3_context c, __in Z3_ast_vector v); + void Z3_API Z3_ast_vector_inc_ref(Z3_context c, Z3_ast_vector v); /** \brief Decrement the reference counter of the given AST vector. def_API('Z3_ast_vector_dec_ref', VOID, (_in(CONTEXT), _in(AST_VECTOR))) */ - void Z3_API Z3_ast_vector_dec_ref(__in Z3_context c, __in Z3_ast_vector v); + void Z3_API Z3_ast_vector_dec_ref(Z3_context c, Z3_ast_vector v); #endif /** @@ -6085,7 +6371,7 @@ END_MLAPI_EXCLUDE def_API('Z3_ast_vector_size', UINT, (_in(CONTEXT), _in(AST_VECTOR))) */ - unsigned Z3_API Z3_ast_vector_size(__in Z3_context c, __in Z3_ast_vector v); + unsigned Z3_API Z3_ast_vector_size(Z3_context c, Z3_ast_vector v); /** \brief Return the AST at position \c i in the AST vector \c v. @@ -6094,7 +6380,7 @@ END_MLAPI_EXCLUDE def_API('Z3_ast_vector_get', AST, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT))) */ - Z3_ast Z3_API Z3_ast_vector_get(__in Z3_context c, __in Z3_ast_vector v, __in unsigned i); + Z3_ast Z3_API Z3_ast_vector_get(Z3_context c, Z3_ast_vector v, unsigned i); /** \brief Update position \c i of the AST vector \c v with the AST \c a. @@ -6103,35 +6389,35 @@ END_MLAPI_EXCLUDE def_API('Z3_ast_vector_set', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT), _in(AST))) */ - void Z3_API Z3_ast_vector_set(__in Z3_context c, __in Z3_ast_vector v, __in unsigned i, __in Z3_ast a); + void Z3_API Z3_ast_vector_set(Z3_context c, Z3_ast_vector v, unsigned i, Z3_ast a); /** \brief Resize the AST vector \c v. def_API('Z3_ast_vector_resize', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT))) */ - void Z3_API Z3_ast_vector_resize(__in Z3_context c, __in Z3_ast_vector v, __in unsigned n); + void Z3_API Z3_ast_vector_resize(Z3_context c, Z3_ast_vector v, unsigned n); /** \brief Add the AST \c a in the end of the AST vector \c v. The size of \c v is increased by one. def_API('Z3_ast_vector_push', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(AST))) */ - void Z3_API Z3_ast_vector_push(__in Z3_context c, __in Z3_ast_vector v, __in Z3_ast a); + void Z3_API Z3_ast_vector_push(Z3_context c, Z3_ast_vector v, Z3_ast a); /** \brief Translate the AST vector \c v from context \c s into an AST vector in context \c t. def_API('Z3_ast_vector_translate', AST_VECTOR, (_in(CONTEXT), _in(AST_VECTOR), _in(CONTEXT))) */ - Z3_ast_vector Z3_API Z3_ast_vector_translate(__in Z3_context s, __in Z3_ast_vector v, __in Z3_context t); + Z3_ast_vector Z3_API Z3_ast_vector_translate(Z3_context s, Z3_ast_vector v, Z3_context t); /** \brief Convert AST vector into a string. def_API('Z3_ast_vector_to_string', STRING, (_in(CONTEXT), _in(AST_VECTOR))) */ - Z3_string Z3_API Z3_ast_vector_to_string(__in Z3_context c, __in Z3_ast_vector v); + Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v); /*@}*/ @@ -6148,7 +6434,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_ast_map', AST_MAP, (_in(CONTEXT),) ) */ - Z3_ast_map Z3_API Z3_mk_ast_map(__in Z3_context c); + Z3_ast_map Z3_API Z3_mk_ast_map(Z3_context c); #ifdef Conly /** @@ -6156,14 +6442,14 @@ END_MLAPI_EXCLUDE def_API('Z3_ast_map_inc_ref', VOID, (_in(CONTEXT), _in(AST_MAP))) */ - void Z3_API Z3_ast_map_inc_ref(__in Z3_context c, __in Z3_ast_map m); + void Z3_API Z3_ast_map_inc_ref(Z3_context c, Z3_ast_map m); /** \brief Decrement the reference counter of the given AST map. def_API('Z3_ast_map_dec_ref', VOID, (_in(CONTEXT), _in(AST_MAP))) */ - void Z3_API Z3_ast_map_dec_ref(__in Z3_context c, __in Z3_ast_map m); + void Z3_API Z3_ast_map_dec_ref(Z3_context c, Z3_ast_map m); #endif /** @@ -6171,7 +6457,7 @@ END_MLAPI_EXCLUDE def_API('Z3_ast_map_contains', BOOL, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ - Z3_bool Z3_API Z3_ast_map_contains(__in Z3_context c, __in Z3_ast_map m, __in Z3_ast k); + Z3_bool Z3_API Z3_ast_map_contains(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Return the value associated with the key \c k. @@ -6180,49 +6466,49 @@ END_MLAPI_EXCLUDE def_API('Z3_ast_map_find', AST, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ - Z3_ast Z3_API Z3_ast_map_find(__in Z3_context c, __in Z3_ast_map m, __in Z3_ast k); + Z3_ast Z3_API Z3_ast_map_find(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Store/Replace a new key, value pair in the given map. def_API('Z3_ast_map_insert', VOID, (_in(CONTEXT), _in(AST_MAP), _in(AST), _in(AST))) */ - void Z3_API Z3_ast_map_insert(__in Z3_context c, __in Z3_ast_map m, __in Z3_ast k, __in Z3_ast v); + void Z3_API Z3_ast_map_insert(Z3_context c, Z3_ast_map m, Z3_ast k, Z3_ast v); /** \brief Erase a key from the map. def_API('Z3_ast_map_erase', VOID, (_in(CONTEXT), _in(AST_MAP), _in(AST))) */ - void Z3_API Z3_ast_map_erase(__in Z3_context c, __in Z3_ast_map m, __in Z3_ast k); + void Z3_API Z3_ast_map_erase(Z3_context c, Z3_ast_map m, Z3_ast k); /** \brief Remove all keys from the given map. def_API('Z3_ast_map_reset', VOID, (_in(CONTEXT), _in(AST_MAP))) */ - void Z3_API Z3_ast_map_reset(__in Z3_context c, __in Z3_ast_map m); + void Z3_API Z3_ast_map_reset(Z3_context c, Z3_ast_map m); /** \brief Return the size of the given map. def_API('Z3_ast_map_size', UINT, (_in(CONTEXT), _in(AST_MAP))) */ - unsigned Z3_API Z3_ast_map_size(__in Z3_context c, __in Z3_ast_map m); + unsigned Z3_API Z3_ast_map_size(Z3_context c, Z3_ast_map m); /** \brief Return the keys stored in the given map. def_API('Z3_ast_map_keys', AST_VECTOR, (_in(CONTEXT), _in(AST_MAP))) */ - Z3_ast_vector Z3_API Z3_ast_map_keys(__in Z3_context c, __in Z3_ast_map m); + Z3_ast_vector Z3_API Z3_ast_map_keys(Z3_context c, Z3_ast_map m); /** \brief Convert the given map into a string. def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP))) */ - Z3_string Z3_API Z3_ast_map_to_string(__in Z3_context c, __in Z3_ast_map m); + Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m); /*@}*/ @@ -6248,7 +6534,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_goal', GOAL, (_in(CONTEXT), _in(BOOL), _in(BOOL), _in(BOOL))) */ - Z3_goal Z3_API Z3_mk_goal(__in Z3_context c, __in Z3_bool models, __in Z3_bool unsat_cores, __in Z3_bool proofs); + Z3_goal Z3_API Z3_mk_goal(Z3_context c, Z3_bool models, Z3_bool unsat_cores, Z3_bool proofs); #ifdef Conly /** @@ -6256,14 +6542,14 @@ END_MLAPI_EXCLUDE def_API('Z3_goal_inc_ref', VOID, (_in(CONTEXT), _in(GOAL))) */ - void Z3_API Z3_goal_inc_ref(__in Z3_context c, __in Z3_goal g); + void Z3_API Z3_goal_inc_ref(Z3_context c, Z3_goal g); /** \brief Decrement the reference counter of the given goal. def_API('Z3_goal_dec_ref', VOID, (_in(CONTEXT), _in(GOAL))) */ - void Z3_API Z3_goal_dec_ref(__in Z3_context c, __in Z3_goal g); + void Z3_API Z3_goal_dec_ref(Z3_context c, Z3_goal g); #endif /** @@ -6273,42 +6559,42 @@ END_MLAPI_EXCLUDE def_API('Z3_goal_precision', UINT, (_in(CONTEXT), _in(GOAL))) */ - Z3_goal_prec Z3_API Z3_goal_precision(__in Z3_context c, __in Z3_goal g); + Z3_goal_prec Z3_API Z3_goal_precision(Z3_context c, Z3_goal g); /** \brief Add a new formula \c a to the given goal. def_API('Z3_goal_assert', VOID, (_in(CONTEXT), _in(GOAL), _in(AST))) */ - void Z3_API Z3_goal_assert(__in Z3_context c, __in Z3_goal g, __in Z3_ast a); + void Z3_API Z3_goal_assert(Z3_context c, Z3_goal g, Z3_ast a); /** \brief Return true if the given goal contains the formula \c false. def_API('Z3_goal_inconsistent', BOOL, (_in(CONTEXT), _in(GOAL))) */ - Z3_bool Z3_API Z3_goal_inconsistent(__in Z3_context c, __in Z3_goal g); + Z3_bool Z3_API Z3_goal_inconsistent(Z3_context c, Z3_goal g); /** \brief Return the depth of the given goal. It tracks how many transformations were applied to it. def_API('Z3_goal_depth', UINT, (_in(CONTEXT), _in(GOAL))) */ - unsigned Z3_API Z3_goal_depth(__in Z3_context c, __in Z3_goal g); + unsigned Z3_API Z3_goal_depth(Z3_context c, Z3_goal g); /** \brief Erase all formulas from the given goal. def_API('Z3_goal_reset', VOID, (_in(CONTEXT), _in(GOAL))) */ - void Z3_API Z3_goal_reset(__in Z3_context c, __in Z3_goal g); + void Z3_API Z3_goal_reset(Z3_context c, Z3_goal g); /** \brief Return the number of formulas in the given goal. def_API('Z3_goal_size', UINT, (_in(CONTEXT), _in(GOAL))) */ - unsigned Z3_API Z3_goal_size(__in Z3_context c, __in Z3_goal g); + unsigned Z3_API Z3_goal_size(Z3_context c, Z3_goal g); /** \brief Return a formula from the given goal. @@ -6317,42 +6603,42 @@ END_MLAPI_EXCLUDE def_API('Z3_goal_formula', AST, (_in(CONTEXT), _in(GOAL), _in(UINT))) */ - Z3_ast Z3_API Z3_goal_formula(__in Z3_context c, __in Z3_goal g, __in unsigned idx); + Z3_ast Z3_API Z3_goal_formula(Z3_context c, Z3_goal g, unsigned idx); /** \brief Return the number of formulas, subformulas and terms in the given goal. def_API('Z3_goal_num_exprs', UINT, (_in(CONTEXT), _in(GOAL))) */ - unsigned Z3_API Z3_goal_num_exprs(__in Z3_context c, __in Z3_goal g); + unsigned Z3_API Z3_goal_num_exprs(Z3_context c, Z3_goal g); /** \brief Return true if the goal is empty, and it is precise or the product of a under approximation. def_API('Z3_goal_is_decided_sat', BOOL, (_in(CONTEXT), _in(GOAL))) */ - Z3_bool Z3_API Z3_goal_is_decided_sat(__in Z3_context c, __in Z3_goal g); + Z3_bool Z3_API Z3_goal_is_decided_sat(Z3_context c, Z3_goal g); /** \brief Return true if the goal contains false, and it is precise or the product of an over approximation. def_API('Z3_goal_is_decided_unsat', BOOL, (_in(CONTEXT), _in(GOAL))) */ - Z3_bool Z3_API Z3_goal_is_decided_unsat(__in Z3_context c, __in Z3_goal g); + Z3_bool Z3_API Z3_goal_is_decided_unsat(Z3_context c, Z3_goal g); /** \brief Copy a goal \c g from the context \c source to a the context \c target. def_API('Z3_goal_translate', GOAL, (_in(CONTEXT), _in(GOAL), _in(CONTEXT))) */ - Z3_goal Z3_API Z3_goal_translate(__in Z3_context source, __in Z3_goal g, __in Z3_context target); + Z3_goal Z3_API Z3_goal_translate(Z3_context source, Z3_goal g, Z3_context target); /** \brief Convert a goal into a string. def_API('Z3_goal_to_string', STRING, (_in(CONTEXT), _in(GOAL))) */ - Z3_string Z3_API Z3_goal_to_string(__in Z3_context c, __in Z3_goal g); + Z3_string Z3_API Z3_goal_to_string(Z3_context c, Z3_goal g); /*@}*/ @@ -6370,7 +6656,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_tactic', TACTIC, (_in(CONTEXT), _in(STRING))) */ - Z3_tactic Z3_API Z3_mk_tactic(__in Z3_context c, __in Z3_string name); + Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name); #ifdef Conly /** @@ -6378,14 +6664,14 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_inc_ref', VOID, (_in(CONTEXT), _in(TACTIC))) */ - void Z3_API Z3_tactic_inc_ref(__in Z3_context c, __in Z3_tactic t); + void Z3_API Z3_tactic_inc_ref(Z3_context c, Z3_tactic t); /** \brief Decrement the reference counter of the given tactic. def_API('Z3_tactic_dec_ref', VOID, (_in(CONTEXT), _in(TACTIC))) */ - void Z3_API Z3_tactic_dec_ref(__in Z3_context c, __in Z3_tactic g); + void Z3_API Z3_tactic_dec_ref(Z3_context c, Z3_tactic g); #endif /** @@ -6398,7 +6684,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_probe', PROBE, (_in(CONTEXT), _in(STRING))) */ - Z3_probe Z3_API Z3_mk_probe(__in Z3_context c, __in Z3_string name); + Z3_probe Z3_API Z3_mk_probe(Z3_context c, Z3_string name); #ifdef Conly /** @@ -6406,14 +6692,14 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_inc_ref', VOID, (_in(CONTEXT), _in(PROBE))) */ - void Z3_API Z3_probe_inc_ref(__in Z3_context c, __in Z3_probe p); + void Z3_API Z3_probe_inc_ref(Z3_context c, Z3_probe p); /** \brief Decrement the reference counter of the given probe. def_API('Z3_probe_dec_ref', VOID, (_in(CONTEXT), _in(PROBE))) */ - void Z3_API Z3_probe_dec_ref(__in Z3_context c, __in Z3_probe p); + void Z3_API Z3_probe_dec_ref(Z3_context c, Z3_probe p); #endif /** @@ -6422,7 +6708,7 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_and_then', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ - Z3_tactic Z3_API Z3_tactic_and_then(__in Z3_context c, __in Z3_tactic t1, __in Z3_tactic t2); + Z3_tactic Z3_API Z3_tactic_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that first applies \c t1 to a given goal, @@ -6430,14 +6716,14 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_or_else', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ - Z3_tactic Z3_API Z3_tactic_or_else(__in Z3_context c, __in Z3_tactic t1, __in Z3_tactic t2); + Z3_tactic Z3_API Z3_tactic_or_else(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that applies the given tactics in parallel. def_API('Z3_tactic_par_or', TACTIC, (_in(CONTEXT), _in(UINT), _in_array(1, TACTIC))) */ - Z3_tactic Z3_API Z3_tactic_par_or(__in Z3_context c, __in unsigned num, __in_ecount(num) Z3_tactic const ts[]); + Z3_tactic Z3_API Z3_tactic_par_or(Z3_context c, unsigned num, Z3_tactic const ts[]); /** \brief Return a tactic that applies \c t1 to a given goal and then \c t2 @@ -6445,7 +6731,7 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_par_and_then', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(TACTIC))) */ - Z3_tactic Z3_API Z3_tactic_par_and_then(__in Z3_context c, __in Z3_tactic t1, __in Z3_tactic t2); + Z3_tactic Z3_API Z3_tactic_par_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that applies \c t to a given goal for \c ms milliseconds. @@ -6453,7 +6739,7 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_try_for', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(UINT))) */ - Z3_tactic Z3_API Z3_tactic_try_for(__in Z3_context c, __in Z3_tactic t, __in unsigned ms); + Z3_tactic Z3_API Z3_tactic_try_for(Z3_context c, Z3_tactic t, unsigned ms); /** \brief Return a tactic that applies \c t to a given goal is the probe \c p evaluates to true. @@ -6461,7 +6747,7 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_when', TACTIC, (_in(CONTEXT), _in(PROBE), _in(TACTIC))) */ - Z3_tactic Z3_API Z3_tactic_when(__in Z3_context c, __in Z3_probe p, __in Z3_tactic t); + Z3_tactic Z3_API Z3_tactic_when(Z3_context c, Z3_probe p, Z3_tactic t); /** \brief Return a tactic that applies \c t1 to a given goal if the probe \c p evaluates to true, @@ -6469,7 +6755,7 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_cond', TACTIC, (_in(CONTEXT), _in(PROBE), _in(TACTIC), _in(TACTIC))) */ - Z3_tactic Z3_API Z3_tactic_cond(__in Z3_context c, __in Z3_probe p, __in Z3_tactic t1, __in Z3_tactic t2); + Z3_tactic Z3_API Z3_tactic_cond(Z3_context c, Z3_probe p, Z3_tactic t1, Z3_tactic t2); /** \brief Return a tactic that keeps applying \c t until the goal is not modified anymore or the maximum @@ -6477,28 +6763,28 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_repeat', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(UINT))) */ - Z3_tactic Z3_API Z3_tactic_repeat(__in Z3_context c, __in Z3_tactic t, unsigned max); + Z3_tactic Z3_API Z3_tactic_repeat(Z3_context c, Z3_tactic t, unsigned max); /** \brief Return a tactic that just return the given goal. def_API('Z3_tactic_skip', TACTIC, (_in(CONTEXT),)) */ - Z3_tactic Z3_API Z3_tactic_skip(__in Z3_context c); + Z3_tactic Z3_API Z3_tactic_skip(Z3_context c); /** \brief Return a tactic that always fails. def_API('Z3_tactic_fail', TACTIC, (_in(CONTEXT),)) */ - Z3_tactic Z3_API Z3_tactic_fail(__in Z3_context c); + Z3_tactic Z3_API Z3_tactic_fail(Z3_context c); /** \brief Return a tactic that fails if the probe \c p evaluates to false. def_API('Z3_tactic_fail_if', TACTIC, (_in(CONTEXT), _in(PROBE))) */ - Z3_tactic Z3_API Z3_tactic_fail_if(__in Z3_context c, __in Z3_probe p); + Z3_tactic Z3_API Z3_tactic_fail_if(Z3_context c, Z3_probe p); /** \brief Return a tactic that fails if the goal is not trivially satisfiable (i.e., empty) or @@ -6506,21 +6792,21 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_fail_if_not_decided', TACTIC, (_in(CONTEXT),)) */ - Z3_tactic Z3_API Z3_tactic_fail_if_not_decided(__in Z3_context c); + Z3_tactic Z3_API Z3_tactic_fail_if_not_decided(Z3_context c); /** \brief Return a tactic that applies \c t using the given set of parameters. def_API('Z3_tactic_using_params', TACTIC, (_in(CONTEXT), _in(TACTIC), _in(PARAMS))) */ - Z3_tactic Z3_API Z3_tactic_using_params(__in Z3_context c, __in Z3_tactic t, __in Z3_params p); + Z3_tactic Z3_API Z3_tactic_using_params(Z3_context c, Z3_tactic t, Z3_params p); /** \brief Return a probe that always evaluates to val. def_API('Z3_probe_const', PROBE, (_in(CONTEXT), _in(DOUBLE))) */ - Z3_probe Z3_API Z3_probe_const(__in Z3_context x, __in double val); + Z3_probe Z3_API Z3_probe_const(Z3_context x, double val); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is less than the value returned by \c p2. @@ -6529,7 +6815,7 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_lt', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ - Z3_probe Z3_API Z3_probe_lt(__in Z3_context x, __in Z3_probe p1, __in Z3_probe p2); + Z3_probe Z3_API Z3_probe_lt(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is greater than the value returned by \c p2. @@ -6538,7 +6824,7 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_gt', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ - Z3_probe Z3_API Z3_probe_gt(__in Z3_context x, __in Z3_probe p1, __in Z3_probe p2); + Z3_probe Z3_API Z3_probe_gt(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is less than or equal to the value returned by \c p2. @@ -6547,7 +6833,7 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_le', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ - Z3_probe Z3_API Z3_probe_le(__in Z3_context x, __in Z3_probe p1, __in Z3_probe p2); + Z3_probe Z3_API Z3_probe_le(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is greater than or equal to the value returned by \c p2. @@ -6556,7 +6842,7 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_ge', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ - Z3_probe Z3_API Z3_probe_ge(__in Z3_context x, __in Z3_probe p1, __in Z3_probe p2); + Z3_probe Z3_API Z3_probe_ge(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when the value returned by \c p1 is equal to the value returned by \c p2. @@ -6565,7 +6851,7 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_eq', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ - Z3_probe Z3_API Z3_probe_eq(__in Z3_context x, __in Z3_probe p1, __in Z3_probe p2); + Z3_probe Z3_API Z3_probe_eq(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p1 and \c p2 evaluates to true. @@ -6574,7 +6860,7 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_and', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ - Z3_probe Z3_API Z3_probe_and(__in Z3_context x, __in Z3_probe p1, __in Z3_probe p2); + Z3_probe Z3_API Z3_probe_and(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p1 or \c p2 evaluates to true. @@ -6583,7 +6869,7 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_or', PROBE, (_in(CONTEXT), _in(PROBE), _in(PROBE))) */ - Z3_probe Z3_API Z3_probe_or(__in Z3_context x, __in Z3_probe p1, __in Z3_probe p2); + Z3_probe Z3_API Z3_probe_or(Z3_context x, Z3_probe p1, Z3_probe p2); /** \brief Return a probe that evaluates to "true" when \c p does not evaluate to true. @@ -6592,14 +6878,14 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_not', PROBE, (_in(CONTEXT), _in(PROBE))) */ - Z3_probe Z3_API Z3_probe_not(__in Z3_context x, __in Z3_probe p); + Z3_probe Z3_API Z3_probe_not(Z3_context x, Z3_probe p); /** \brief Return the number of builtin tactics available in Z3. def_API('Z3_get_num_tactics', UINT, (_in(CONTEXT),)) */ - unsigned Z3_API Z3_get_num_tactics(__in Z3_context c); + unsigned Z3_API Z3_get_num_tactics(Z3_context c); /** \brief Return the name of the idx tactic. @@ -6608,14 +6894,14 @@ END_MLAPI_EXCLUDE def_API('Z3_get_tactic_name', STRING, (_in(CONTEXT), _in(UINT))) */ - Z3_string Z3_API Z3_get_tactic_name(__in Z3_context c, unsigned i); + Z3_string Z3_API Z3_get_tactic_name(Z3_context c, unsigned i); /** \brief Return the number of builtin probes available in Z3. def_API('Z3_get_num_probes', UINT, (_in(CONTEXT),)) */ - unsigned Z3_API Z3_get_num_probes(__in Z3_context c); + unsigned Z3_API Z3_get_num_probes(Z3_context c); /** \brief Return the name of the i probe. @@ -6624,35 +6910,35 @@ END_MLAPI_EXCLUDE def_API('Z3_get_probe_name', STRING, (_in(CONTEXT), _in(UINT))) */ - Z3_string Z3_API Z3_get_probe_name(__in Z3_context c, unsigned i); + Z3_string Z3_API Z3_get_probe_name(Z3_context c, unsigned i); /** \brief Return a string containing a description of parameters accepted by the given tactic. def_API('Z3_tactic_get_help', STRING, (_in(CONTEXT), _in(TACTIC))) */ - Z3_string Z3_API Z3_tactic_get_help(__in Z3_context c, __in Z3_tactic t); + Z3_string Z3_API Z3_tactic_get_help(Z3_context c, Z3_tactic t); /** \brief Return the parameter description set for the given tactic object. def_API('Z3_tactic_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(TACTIC))) */ - Z3_param_descrs Z3_API Z3_tactic_get_param_descrs(__in Z3_context c, __in Z3_tactic t); + Z3_param_descrs Z3_API Z3_tactic_get_param_descrs(Z3_context c, Z3_tactic t); /** \brief Return a string containing a description of the tactic with the given name. def_API('Z3_tactic_get_descr', STRING, (_in(CONTEXT), _in(STRING))) */ - Z3_string Z3_API Z3_tactic_get_descr(__in Z3_context c, __in Z3_string name); + Z3_string Z3_API Z3_tactic_get_descr(Z3_context c, Z3_string name); /** \brief Return a string containing a description of the probe with the given name. def_API('Z3_probe_get_descr', STRING, (_in(CONTEXT), _in(STRING))) */ - Z3_string Z3_API Z3_probe_get_descr(__in Z3_context c, __in Z3_string name); + Z3_string Z3_API Z3_probe_get_descr(Z3_context c, Z3_string name); /** \brief Execute the probe over the goal. The probe always produce a double value. @@ -6660,14 +6946,14 @@ END_MLAPI_EXCLUDE def_API('Z3_probe_apply', DOUBLE, (_in(CONTEXT), _in(PROBE), _in(GOAL))) */ - double Z3_API Z3_probe_apply(__in Z3_context c, __in Z3_probe p, __in Z3_goal g); + double Z3_API Z3_probe_apply(Z3_context c, Z3_probe p, Z3_goal g); /** \brief Apply tactic \c t to the goal \c g. def_API('Z3_tactic_apply', APPLY_RESULT, (_in(CONTEXT), _in(TACTIC), _in(GOAL))) */ - Z3_apply_result Z3_API Z3_tactic_apply(__in Z3_context c, __in Z3_tactic t, __in Z3_goal g); + Z3_apply_result Z3_API Z3_tactic_apply(Z3_context c, Z3_tactic t, Z3_goal g); /** \brief Apply tactic \c t to the goal \c g using the parameter set \c p. @@ -6682,14 +6968,14 @@ END_MLAPI_EXCLUDE def_API('Z3_apply_result_inc_ref', VOID, (_in(CONTEXT), _in(APPLY_RESULT))) */ - void Z3_API Z3_apply_result_inc_ref(__in Z3_context c, __in Z3_apply_result r); + void Z3_API Z3_apply_result_inc_ref(Z3_context c, Z3_apply_result r); /** \brief Decrement the reference counter of the given \c Z3_apply_result object. def_API('Z3_apply_result_dec_ref', VOID, (_in(CONTEXT), _in(APPLY_RESULT))) */ - void Z3_API Z3_apply_result_dec_ref(__in Z3_context c, __in Z3_apply_result r); + void Z3_API Z3_apply_result_dec_ref(Z3_context c, Z3_apply_result r); #endif /** @@ -6697,14 +6983,14 @@ END_MLAPI_EXCLUDE def_API('Z3_apply_result_to_string', STRING, (_in(CONTEXT), _in(APPLY_RESULT))) */ - Z3_string Z3_API Z3_apply_result_to_string(__in Z3_context c, __in Z3_apply_result r); + Z3_string Z3_API Z3_apply_result_to_string(Z3_context c, Z3_apply_result r); /** \brief Return the number of subgoals in the \c Z3_apply_result object returned by #Z3_tactic_apply. def_API('Z3_apply_result_get_num_subgoals', UINT, (_in(CONTEXT), _in(APPLY_RESULT))) */ - unsigned Z3_API Z3_apply_result_get_num_subgoals(__in Z3_context c, __in Z3_apply_result r); + unsigned Z3_API Z3_apply_result_get_num_subgoals(Z3_context c, Z3_apply_result r); /** \brief Return one of the subgoals in the \c Z3_apply_result object returned by #Z3_tactic_apply. @@ -6713,7 +6999,7 @@ END_MLAPI_EXCLUDE def_API('Z3_apply_result_get_subgoal', GOAL, (_in(CONTEXT), _in(APPLY_RESULT), _in(UINT))) */ - Z3_goal Z3_API Z3_apply_result_get_subgoal(__in Z3_context c, __in Z3_apply_result r, __in unsigned i); + Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i); /** \brief Convert a model for the subgoal \c Z3_apply_result_get_subgoal(c, r, i) into a model for the original goal \c g. @@ -6721,7 +7007,7 @@ END_MLAPI_EXCLUDE def_API('Z3_apply_result_convert_model', MODEL, (_in(CONTEXT), _in(APPLY_RESULT), _in(UINT), _in(MODEL))) */ - Z3_model Z3_API Z3_apply_result_convert_model(__in Z3_context c, __in Z3_apply_result r, __in unsigned i, __in Z3_model m); + Z3_model Z3_API Z3_apply_result_convert_model(Z3_context c, Z3_apply_result r, unsigned i, Z3_model m); /*@}*/ @@ -6740,7 +7026,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_solver', SOLVER, (_in(CONTEXT),)) */ - Z3_solver Z3_API Z3_mk_solver(__in Z3_context c); + Z3_solver Z3_API Z3_mk_solver(Z3_context c); /** \brief Create a new (incremental) solver. @@ -6754,7 +7040,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_simple_solver', SOLVER, (_in(CONTEXT),)) */ - Z3_solver Z3_API Z3_mk_simple_solver(__in Z3_context c); + Z3_solver Z3_API Z3_mk_simple_solver(Z3_context c); /** \brief Create a new solver customized for the given logic. @@ -6765,7 +7051,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_solver_for_logic', SOLVER, (_in(CONTEXT), _in(SYMBOL))) */ - Z3_solver Z3_API Z3_mk_solver_for_logic(__in Z3_context c, __in Z3_symbol logic); + Z3_solver Z3_API Z3_mk_solver_for_logic(Z3_context c, Z3_symbol logic); /** \brief Create a new solver that is implemented using the given tactic. @@ -6774,28 +7060,28 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_solver_from_tactic', SOLVER, (_in(CONTEXT), _in(TACTIC))) */ - Z3_solver Z3_API Z3_mk_solver_from_tactic(__in Z3_context c, __in Z3_tactic t); + Z3_solver Z3_API Z3_mk_solver_from_tactic(Z3_context c, Z3_tactic t); /** \brief Return a string describing all solver available parameters. def_API('Z3_solver_get_help', STRING, (_in(CONTEXT), _in(SOLVER))) */ - Z3_string Z3_API Z3_solver_get_help(__in Z3_context c, __in Z3_solver s); + Z3_string Z3_API Z3_solver_get_help(Z3_context c, Z3_solver s); /** \brief Return the parameter description set for the given solver object. def_API('Z3_solver_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(SOLVER))) */ - Z3_param_descrs Z3_API Z3_solver_get_param_descrs(__in Z3_context c, __in Z3_solver s); + Z3_param_descrs Z3_API Z3_solver_get_param_descrs(Z3_context c, Z3_solver s); /** \brief Set the given solver using the given parameters. def_API('Z3_solver_set_params', VOID, (_in(CONTEXT), _in(SOLVER), _in(PARAMS))) */ - void Z3_API Z3_solver_set_params(__in Z3_context c, __in Z3_solver s, __in Z3_params p); + void Z3_API Z3_solver_set_params(Z3_context c, Z3_solver s, Z3_params p); #ifdef Conly /** @@ -6803,14 +7089,14 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_inc_ref', VOID, (_in(CONTEXT), _in(SOLVER))) */ - void Z3_API Z3_solver_inc_ref(__in Z3_context c, __in Z3_solver s); + void Z3_API Z3_solver_inc_ref(Z3_context c, Z3_solver s); /** \brief Decrement the reference counter of the given solver. def_API('Z3_solver_dec_ref', VOID, (_in(CONTEXT), _in(SOLVER))) */ - void Z3_API Z3_solver_dec_ref(__in Z3_context c, __in Z3_solver s); + void Z3_API Z3_solver_dec_ref(Z3_context c, Z3_solver s); #endif /** @@ -6822,7 +7108,7 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_push', VOID, (_in(CONTEXT), _in(SOLVER))) */ - void Z3_API Z3_solver_push(__in Z3_context c, __in Z3_solver s); + void Z3_API Z3_solver_push(Z3_context c, Z3_solver s); /** \brief Backtrack \c n backtracking points. @@ -6833,14 +7119,14 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_pop', VOID, (_in(CONTEXT), _in(SOLVER), _in(UINT))) */ - void Z3_API Z3_solver_pop(__in Z3_context c, __in Z3_solver s, unsigned n); + void Z3_API Z3_solver_pop(Z3_context c, Z3_solver s, unsigned n); /** \brief Remove all assertions from the solver. def_API('Z3_solver_reset', VOID, (_in(CONTEXT), _in(SOLVER))) */ - void Z3_API Z3_solver_reset(__in Z3_context c, __in Z3_solver s); + void Z3_API Z3_solver_reset(Z3_context c, Z3_solver s); /** \brief Return the number of backtracking points. @@ -6850,7 +7136,7 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_get_num_scopes', UINT, (_in(CONTEXT), _in(SOLVER))) */ - unsigned Z3_API Z3_solver_get_num_scopes(__in Z3_context c, __in Z3_solver s); + unsigned Z3_API Z3_solver_get_num_scopes(Z3_context c, Z3_solver s); /** \brief Assert a constraint into the solver. @@ -6860,7 +7146,7 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_assert', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST))) */ - void Z3_API Z3_solver_assert(__in Z3_context c, __in Z3_solver s, __in Z3_ast a); + void Z3_API Z3_solver_assert(Z3_context c, Z3_solver s, Z3_ast a); /** \brief Assert a constraint \c a into the solver, and track it (in the unsat) core using @@ -6876,14 +7162,14 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_assert_and_track', VOID, (_in(CONTEXT), _in(SOLVER), _in(AST), _in(AST))) */ - void Z3_API Z3_solver_assert_and_track(__in Z3_context c, __in Z3_solver s, __in Z3_ast a, __in Z3_ast p); + void Z3_API Z3_solver_assert_and_track(Z3_context c, Z3_solver s, Z3_ast a, Z3_ast p); /** \brief Return the set of asserted formulas as a goal object. def_API('Z3_solver_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ - Z3_ast_vector Z3_API Z3_solver_get_assertions(__in Z3_context c, __in Z3_solver s); + Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s); /** \brief Check whether the assertions in a given solver are consistent or not. @@ -6901,7 +7187,7 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_check', INT, (_in(CONTEXT), _in(SOLVER))) */ - Z3_lbool Z3_API Z3_solver_check(__in Z3_context c, __in Z3_solver s); + Z3_lbool Z3_API Z3_solver_check(Z3_context c, Z3_solver s); /** \brief Check whether the assertions in the given solver and @@ -6914,8 +7200,8 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_check_assumptions', INT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST))) */ - Z3_lbool Z3_API Z3_solver_check_assumptions(__in Z3_context c, __in Z3_solver s, - __in unsigned num_assumptions, __in_ecount(num_assumptions) Z3_ast const assumptions[]); + Z3_lbool Z3_API Z3_solver_check_assumptions(Z3_context c, Z3_solver s, + unsigned num_assumptions, Z3_ast const assumptions[]); /** \brief Retrieve the model for the last #Z3_solver_check or #Z3_solver_check_assumptions @@ -6925,7 +7211,7 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_get_model', MODEL, (_in(CONTEXT), _in(SOLVER))) */ - Z3_model Z3_API Z3_solver_get_model(__in Z3_context c, __in Z3_solver s); + Z3_model Z3_API Z3_solver_get_model(Z3_context c, Z3_solver s); /** \brief Retrieve the proof for the last #Z3_solver_check or #Z3_solver_check_assumptions @@ -6936,7 +7222,7 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_get_proof', AST, (_in(CONTEXT), _in(SOLVER))) */ - Z3_ast Z3_API Z3_solver_get_proof(__in Z3_context c, __in Z3_solver s); + Z3_ast Z3_API Z3_solver_get_proof(Z3_context c, Z3_solver s); /** \brief Retrieve the unsat core for the last #Z3_solver_check_assumptions @@ -6944,7 +7230,7 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_get_unsat_core', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ - Z3_ast_vector Z3_API Z3_solver_get_unsat_core(__in Z3_context c, __in Z3_solver s); + Z3_ast_vector Z3_API Z3_solver_get_unsat_core(Z3_context c, Z3_solver s); /** \brief Return a brief justification for an "unknown" result (i.e., Z3_L_UNDEF) for @@ -6952,7 +7238,7 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_get_reason_unknown', STRING, (_in(CONTEXT), _in(SOLVER))) */ - Z3_string Z3_API Z3_solver_get_reason_unknown(__in Z3_context c, __in Z3_solver s); + Z3_string Z3_API Z3_solver_get_reason_unknown(Z3_context c, Z3_solver s); /** \brief Return statistics for the given solver. @@ -6961,14 +7247,14 @@ END_MLAPI_EXCLUDE def_API('Z3_solver_get_statistics', STATS, (_in(CONTEXT), _in(SOLVER))) */ - Z3_stats Z3_API Z3_solver_get_statistics(__in Z3_context c, __in Z3_solver s); + Z3_stats Z3_API Z3_solver_get_statistics(Z3_context c, Z3_solver s); /** \brief Convert a solver into a string. def_API('Z3_solver_to_string', STRING, (_in(CONTEXT), _in(SOLVER))) */ - Z3_string Z3_API Z3_solver_to_string(__in Z3_context c, __in Z3_solver s); + Z3_string Z3_API Z3_solver_to_string(Z3_context c, Z3_solver s); /*@}*/ @@ -6985,7 +7271,7 @@ END_MLAPI_EXCLUDE def_API('Z3_stats_to_string', STRING, (_in(CONTEXT), _in(STATS))) */ - Z3_string Z3_API Z3_stats_to_string(__in Z3_context c, __in Z3_stats s); + Z3_string Z3_API Z3_stats_to_string(Z3_context c, Z3_stats s); /** \mlonly {4 {L Low-level API}} \endmlonly @@ -6997,14 +7283,14 @@ END_MLAPI_EXCLUDE def_API('Z3_stats_inc_ref', VOID, (_in(CONTEXT), _in(STATS))) */ - void Z3_API Z3_stats_inc_ref(__in Z3_context c, __in Z3_stats s); + void Z3_API Z3_stats_inc_ref(Z3_context c, Z3_stats s); /** \brief Decrement the reference counter of the given statistics object. def_API('Z3_stats_dec_ref', VOID, (_in(CONTEXT), _in(STATS))) */ - void Z3_API Z3_stats_dec_ref(__in Z3_context c, __in Z3_stats s); + void Z3_API Z3_stats_dec_ref(Z3_context c, Z3_stats s); #endif /** @@ -7012,7 +7298,7 @@ END_MLAPI_EXCLUDE def_API('Z3_stats_size', UINT, (_in(CONTEXT), _in(STATS))) */ - unsigned Z3_API Z3_stats_size(__in Z3_context c, __in Z3_stats s); + unsigned Z3_API Z3_stats_size(Z3_context c, Z3_stats s); /** \brief Return the key (a string) for a particular statistical data. @@ -7021,7 +7307,7 @@ END_MLAPI_EXCLUDE def_API('Z3_stats_get_key', STRING, (_in(CONTEXT), _in(STATS), _in(UINT))) */ - Z3_string Z3_API Z3_stats_get_key(__in Z3_context c, __in Z3_stats s, __in unsigned idx); + Z3_string Z3_API Z3_stats_get_key(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return Z3_TRUE if the given statistical data is a unsigned integer. @@ -7030,7 +7316,7 @@ END_MLAPI_EXCLUDE def_API('Z3_stats_is_uint', BOOL, (_in(CONTEXT), _in(STATS), _in(UINT))) */ - Z3_bool Z3_API Z3_stats_is_uint(__in Z3_context c, __in Z3_stats s, __in unsigned idx); + Z3_bool Z3_API Z3_stats_is_uint(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return Z3_TRUE if the given statistical data is a double. @@ -7039,7 +7325,7 @@ END_MLAPI_EXCLUDE def_API('Z3_stats_is_double', BOOL, (_in(CONTEXT), _in(STATS), _in(UINT))) */ - Z3_bool Z3_API Z3_stats_is_double(__in Z3_context c, __in Z3_stats s, __in unsigned idx); + Z3_bool Z3_API Z3_stats_is_double(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return the unsigned value of the given statistical data. @@ -7048,7 +7334,7 @@ END_MLAPI_EXCLUDE def_API('Z3_stats_get_uint_value', UINT, (_in(CONTEXT), _in(STATS), _in(UINT))) */ - unsigned Z3_API Z3_stats_get_uint_value(__in Z3_context c, __in Z3_stats s, __in unsigned idx); + unsigned Z3_API Z3_stats_get_uint_value(Z3_context c, Z3_stats s, unsigned idx); /** \brief Return the double value of the given statistical data. @@ -7057,7 +7343,7 @@ END_MLAPI_EXCLUDE def_API('Z3_stats_get_double_value', DOUBLE, (_in(CONTEXT), _in(STATS), _in(UINT))) */ - double Z3_API Z3_stats_get_double_value(__in Z3_context c, __in Z3_stats s, __in unsigned idx); + double Z3_API Z3_stats_get_double_value(Z3_context c, Z3_stats s, unsigned idx); /*@}*/ #endif @@ -7080,10 +7366,10 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_injective_function', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT))) */ Z3_func_decl Z3_API Z3_mk_injective_function( - __in Z3_context c, - __in Z3_symbol s, - unsigned domain_size, __in_ecount(domain_size) Z3_sort const domain[], - __in Z3_sort range + Z3_context c, + Z3_symbol s, + unsigned domain_size, Z3_sort const domain[], + Z3_sort range ); /*@}*/ @@ -7106,7 +7392,7 @@ END_MLAPI_EXCLUDE def_API('Z3_set_logic', VOID, (_in(CONTEXT), _in(STRING))) */ - Z3_bool Z3_API Z3_set_logic(__in Z3_context c, __in Z3_string logic); + Z3_bool Z3_API Z3_set_logic(Z3_context c, Z3_string logic); /** \brief Create a backtracking point. @@ -7121,7 +7407,7 @@ END_MLAPI_EXCLUDE def_API('Z3_push', VOID, (_in(CONTEXT),)) */ - void Z3_API Z3_push(__in Z3_context c); + void Z3_API Z3_push(Z3_context c); /** \brief Backtrack. @@ -7138,7 +7424,7 @@ END_MLAPI_EXCLUDE def_API('Z3_pop', VOID, (_in(CONTEXT), _in(UINT))) */ - void Z3_API Z3_pop(__in Z3_context c, __in unsigned num_scopes); + void Z3_API Z3_pop(Z3_context c, unsigned num_scopes); /** \brief Retrieve the current scope level. @@ -7152,7 +7438,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_num_scopes', UINT, (_in(CONTEXT),)) */ - unsigned Z3_API Z3_get_num_scopes(__in Z3_context c); + unsigned Z3_API Z3_get_num_scopes(Z3_context c); /** \conly \brief Persist AST through num_scopes pops. @@ -7175,7 +7461,7 @@ END_MLAPI_EXCLUDE def_API('Z3_persist_ast', VOID, (_in(CONTEXT), _in(AST), _in(UINT))) */ - void Z3_API Z3_persist_ast(__in Z3_context c, __in Z3_ast a, __in unsigned num_scopes); + void Z3_API Z3_persist_ast(Z3_context c, Z3_ast a, unsigned num_scopes); /** \brief Assert a constraint into the logical context. @@ -7193,7 +7479,7 @@ END_MLAPI_EXCLUDE def_API('Z3_assert_cnstr', VOID, (_in(CONTEXT), _in(AST))) */ - void Z3_API Z3_assert_cnstr(__in Z3_context c, __in Z3_ast a); + void Z3_API Z3_assert_cnstr(Z3_context c, Z3_ast a); /** \brief Check whether the given logical context is consistent or not. @@ -7218,7 +7504,7 @@ END_MLAPI_EXCLUDE def_API('Z3_check_and_get_model', INT, (_in(CONTEXT), _out(MODEL))) */ - Z3_lbool Z3_API Z3_check_and_get_model(__in Z3_context c, __out Z3_model * m); + Z3_lbool Z3_API Z3_check_and_get_model(Z3_context c, Z3_model * m); /** \brief Check whether the given logical context is consistent or not. @@ -7231,7 +7517,7 @@ END_MLAPI_EXCLUDE def_API('Z3_check', INT, (_in(CONTEXT),)) */ - Z3_lbool Z3_API Z3_check(__in Z3_context c); + Z3_lbool Z3_API Z3_check(Z3_context c); /** \brief Check whether the given logical context and optional assumptions is consistent or not. @@ -7272,10 +7558,10 @@ END_MLAPI_EXCLUDE def_API('Z3_check_assumptions', INT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _out(MODEL), _out(AST), _out(UINT), _out_array2(1, 5, AST))) */ Z3_lbool Z3_API Z3_check_assumptions( - __in Z3_context c, - __in unsigned num_assumptions, __in_ecount(num_assumptions) Z3_ast const assumptions[], - __out Z3_model * m, __out Z3_ast* proof, - __inout unsigned* core_size, __inout_ecount(num_assumptions) Z3_ast core[] + Z3_context c, + unsigned num_assumptions, Z3_ast const assumptions[], + Z3_model * m, Z3_ast* proof, + unsigned* core_size, Z3_ast core[] ); #endif @@ -7304,11 +7590,11 @@ END_MLAPI_EXCLUDE def_API('Z3_get_implied_equalities', UINT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST), _out_array(2, UINT))) */ Z3_lbool Z3_API Z3_get_implied_equalities( - __in Z3_context c, - __in Z3_solver s, - __in unsigned num_terms, - __in_ecount(num_terms) Z3_ast const terms[], - __out_ecount(num_terms) unsigned class_ids[] + Z3_context c, + Z3_solver s, + unsigned num_terms, + Z3_ast const terms[], + unsigned class_ids[] ); #endif @@ -7327,7 +7613,7 @@ END_MLAPI_EXCLUDE def_API('Z3_del_model', VOID, (_in(CONTEXT), _in(MODEL))) */ - void Z3_API Z3_del_model(__in Z3_context c, __in Z3_model m); + void Z3_API Z3_del_model(Z3_context c, Z3_model m); /*@}*/ @@ -7347,7 +7633,7 @@ END_MLAPI_EXCLUDE def_API('Z3_soft_check_cancel', VOID, (_in(CONTEXT), )) */ - void Z3_API Z3_soft_check_cancel(__in Z3_context c); + void Z3_API Z3_soft_check_cancel(Z3_context c); /** \brief Retrieve reason for search failure. @@ -7359,7 +7645,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_search_failure', UINT, (_in(CONTEXT), )) */ - Z3_search_failure Z3_API Z3_get_search_failure(__in Z3_context c); + Z3_search_failure Z3_API Z3_get_search_failure(Z3_context c); /*@}*/ @@ -7386,7 +7672,7 @@ END_MLAPI_EXCLUDE def_API('Z3_mk_label', AST, (_in(CONTEXT), _in(SYMBOL), _in(BOOL), _in(AST))) */ - Z3_ast Z3_API Z3_mk_label(__in Z3_context c, __in Z3_symbol s, Z3_bool is_pos, Z3_ast f); + Z3_ast Z3_API Z3_mk_label(Z3_context c, Z3_symbol s, Z3_bool is_pos, Z3_ast f); /** \brief Retrieve the set of labels that were relevant in @@ -7401,7 +7687,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_relevant_labels', LITERALS, (_in(CONTEXT), )) */ - Z3_literals Z3_API Z3_get_relevant_labels(__in Z3_context c); + Z3_literals Z3_API Z3_get_relevant_labels(Z3_context c); /** \brief Retrieve the set of literals that satisfy the current context. @@ -7415,7 +7701,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_relevant_literals', LITERALS, (_in(CONTEXT), )) */ - Z3_literals Z3_API Z3_get_relevant_literals(__in Z3_context c); + Z3_literals Z3_API Z3_get_relevant_literals(Z3_context c); /** \brief Retrieve the set of literals that whose assignment were @@ -7430,7 +7716,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_guessed_literals', LITERALS, (_in(CONTEXT), )) */ - Z3_literals Z3_API Z3_get_guessed_literals(__in Z3_context c); + Z3_literals Z3_API Z3_get_guessed_literals(Z3_context c); /** \brief Delete a labels context. @@ -7441,7 +7727,7 @@ END_MLAPI_EXCLUDE def_API('Z3_del_literals', VOID, (_in(CONTEXT), _in(LITERALS))) */ - void Z3_API Z3_del_literals(__in Z3_context c, __in Z3_literals lbls); + void Z3_API Z3_del_literals(Z3_context c, Z3_literals lbls); /** \brief Retrieve the number of label symbols that were returned. @@ -7452,7 +7738,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_num_literals', UINT, (_in(CONTEXT), _in(LITERALS))) */ - unsigned Z3_API Z3_get_num_literals(__in Z3_context c, __in Z3_literals lbls); + unsigned Z3_API Z3_get_num_literals(Z3_context c, Z3_literals lbls); /** \brief Retrieve label symbol at idx. @@ -7461,7 +7747,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_label_symbol', SYMBOL, (_in(CONTEXT), _in(LITERALS), _in(UINT))) */ - Z3_symbol Z3_API Z3_get_label_symbol(__in Z3_context c, __in Z3_literals lbls, __in unsigned idx); + Z3_symbol Z3_API Z3_get_label_symbol(Z3_context c, Z3_literals lbls, unsigned idx); /** \brief Retrieve literal expression at idx. @@ -7470,7 +7756,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_literal', AST, (_in(CONTEXT), _in(LITERALS), _in(UINT))) */ - Z3_ast Z3_API Z3_get_literal(__in Z3_context c, __in Z3_literals lbls, __in unsigned idx); + Z3_ast Z3_API Z3_get_literal(Z3_context c, Z3_literals lbls, unsigned idx); /** \brief Disable label. @@ -7483,7 +7769,7 @@ END_MLAPI_EXCLUDE def_API('Z3_disable_literal', VOID, (_in(CONTEXT), _in(LITERALS), _in(UINT))) */ - void Z3_API Z3_disable_literal(__in Z3_context c, __in Z3_literals lbls, __in unsigned idx); + void Z3_API Z3_disable_literal(Z3_context c, Z3_literals lbls, unsigned idx); /** \brief Block subsequent checks using the remaining enabled labels. @@ -7492,7 +7778,7 @@ END_MLAPI_EXCLUDE def_API('Z3_block_literals', VOID, (_in(CONTEXT), _in(LITERALS))) */ - void Z3_API Z3_block_literals(__in Z3_context c, __in Z3_literals lbls); + void Z3_API Z3_block_literals(Z3_context c, Z3_literals lbls); /*@}*/ @@ -7512,7 +7798,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_num_constants', UINT, (_in(CONTEXT), _in(MODEL))) */ - unsigned Z3_API Z3_get_model_num_constants(__in Z3_context c, __in Z3_model m); + unsigned Z3_API Z3_get_model_num_constants(Z3_context c, Z3_model m); /** \brief \mlh get_model_constant c m i \endmlh @@ -7526,7 +7812,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_constant', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ - Z3_func_decl Z3_API Z3_get_model_constant(__in Z3_context c, __in Z3_model m, __in unsigned i); + Z3_func_decl Z3_API Z3_get_model_constant(Z3_context c, Z3_model m, unsigned i); /** \brief Return the number of function interpretations in the given model. @@ -7538,7 +7824,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_num_funcs', UINT, (_in(CONTEXT), _in(MODEL))) */ - unsigned Z3_API Z3_get_model_num_funcs(__in Z3_context c, __in Z3_model m); + unsigned Z3_API Z3_get_model_num_funcs(Z3_context c, Z3_model m); /** \brief \mlh get_model_func_decl c m i \endmlh @@ -7552,7 +7838,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_func_decl', FUNC_DECL, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ - Z3_func_decl Z3_API Z3_get_model_func_decl(__in Z3_context c, __in Z3_model m, __in unsigned i); + Z3_func_decl Z3_API Z3_get_model_func_decl(Z3_context c, Z3_model m, unsigned i); /** \brief Return the value of the given constant or function @@ -7562,7 +7848,7 @@ END_MLAPI_EXCLUDE def_API('Z3_eval_func_decl', BOOL, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _out(AST))) */ - Z3_bool Z3_API Z3_eval_func_decl(__in Z3_context c, __in Z3_model m, __in Z3_func_decl decl, __out Z3_ast* v); + Z3_bool Z3_API Z3_eval_func_decl(Z3_context c, Z3_model m, Z3_func_decl decl, Z3_ast* v); /** \brief \mlh is_array_value c v \endmlh @@ -7579,7 +7865,7 @@ END_MLAPI_EXCLUDE def_API('Z3_is_array_value', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _out(UINT))) */ - Z3_bool Z3_API Z3_is_array_value(__in Z3_context c, __in Z3_model m, __in Z3_ast v, __out unsigned* num_entries); + Z3_bool Z3_API Z3_is_array_value(Z3_context c, Z3_model m, Z3_ast v, unsigned* num_entries); /** \brief \mlh get_array_value c v \endmlh @@ -7592,13 +7878,13 @@ END_MLAPI_EXCLUDE def_API('Z3_get_array_value', VOID, (_in(CONTEXT), _in(MODEL), _in(AST), _in(UINT), _out_array(3, AST), _out_array(3, AST), _out (AST))) */ - void Z3_API Z3_get_array_value(__in Z3_context c, - __in Z3_model m, - __in Z3_ast v, - __in unsigned num_entries, - __inout_ecount(num_entries) Z3_ast indices[], - __inout_ecount(num_entries) Z3_ast values[], - __out Z3_ast* else_value + void Z3_API Z3_get_array_value(Z3_context c, + Z3_model m, + Z3_ast v, + unsigned num_entries, + Z3_ast indices[], + Z3_ast values[], + Z3_ast* else_value ); /** @@ -7620,7 +7906,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_func_else', AST, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ - Z3_ast Z3_API Z3_get_model_func_else(__in Z3_context c, __in Z3_model m, __in unsigned i); + Z3_ast Z3_API Z3_get_model_func_else(Z3_context c, Z3_model m, unsigned i); /** \brief \mlh get_model_func_num_entries c m i \endmlh @@ -7641,7 +7927,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_func_num_entries', UINT, (_in(CONTEXT), _in(MODEL), _in(UINT))) */ - unsigned Z3_API Z3_get_model_func_num_entries(__in Z3_context c, __in Z3_model m, __in unsigned i); + unsigned Z3_API Z3_get_model_func_num_entries(Z3_context c, Z3_model m, unsigned i); /** \brief \mlh get_model_func_entry_num_args c m i j \endmlh @@ -7667,10 +7953,10 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_func_entry_num_args', UINT, (_in(CONTEXT), _in(MODEL), _in(UINT), _in(UINT))) */ - unsigned Z3_API Z3_get_model_func_entry_num_args(__in Z3_context c, - __in Z3_model m, - __in unsigned i, - __in unsigned j); + unsigned Z3_API Z3_get_model_func_entry_num_args(Z3_context c, + Z3_model m, + unsigned i, + unsigned j); /** \brief \mlh get_model_func_entry_arg c m i j k \endmlh @@ -7697,11 +7983,11 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_func_entry_arg', AST, (_in(CONTEXT), _in(MODEL), _in(UINT), _in(UINT), _in(UINT))) */ - Z3_ast Z3_API Z3_get_model_func_entry_arg(__in Z3_context c, - __in Z3_model m, - __in unsigned i, - __in unsigned j, - __in unsigned k); + Z3_ast Z3_API Z3_get_model_func_entry_arg(Z3_context c, + Z3_model m, + unsigned i, + unsigned j, + unsigned k); /** \brief \mlh get_model_func_entry_value c m i j \endmlh @@ -7726,10 +8012,10 @@ END_MLAPI_EXCLUDE def_API('Z3_get_model_func_entry_value', AST, (_in(CONTEXT), _in(MODEL), _in(UINT), _in(UINT))) */ - Z3_ast Z3_API Z3_get_model_func_entry_value(__in Z3_context c, - __in Z3_model m, - __in unsigned i, - __in unsigned j); + Z3_ast Z3_API Z3_get_model_func_entry_value(Z3_context c, + Z3_model m, + unsigned i, + unsigned j); /** \brief \mlh eval c m t \endmlh @@ -7750,7 +8036,7 @@ END_MLAPI_EXCLUDE def_API('Z3_eval', BOOL, (_in(CONTEXT), _in(MODEL), _in(AST), _out(AST))) */ - Z3_bool Z3_API Z3_eval(__in Z3_context c, __in Z3_model m, __in Z3_ast t, __out Z3_ast * v); + Z3_bool Z3_API Z3_eval(Z3_context c, Z3_model m, Z3_ast t, Z3_ast * v); /** \brief Evaluate declaration given values. @@ -7762,11 +8048,11 @@ END_MLAPI_EXCLUDE def_API('Z3_eval_decl', BOOL, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL), _in(UINT), _in_array(3, AST), _out(AST))) */ - Z3_bool Z3_API Z3_eval_decl(__in Z3_context c, __in Z3_model m, - __in Z3_func_decl d, - __in unsigned num_args, - __in_ecount(num_args) Z3_ast const args[], - __out Z3_ast* v); + Z3_bool Z3_API Z3_eval_decl(Z3_context c, Z3_model m, + Z3_func_decl d, + unsigned num_args, + Z3_ast const args[], + Z3_ast* v); /*@}*/ @@ -7790,7 +8076,7 @@ END_MLAPI_EXCLUDE def_API('Z3_context_to_string', STRING, (_in(CONTEXT),)) */ - Z3_string Z3_API Z3_context_to_string(__in Z3_context c); + Z3_string Z3_API Z3_context_to_string(Z3_context c); /** \brief Return runtime statistics as a string. @@ -7807,7 +8093,7 @@ END_MLAPI_EXCLUDE def_API('Z3_statistics_to_string', STRING, (_in(CONTEXT),)) */ - Z3_string Z3_API Z3_statistics_to_string(__in Z3_context c); + Z3_string Z3_API Z3_statistics_to_string(Z3_context c); /** \brief Extract satisfying assignment from context as a conjunction. @@ -7822,7 +8108,7 @@ END_MLAPI_EXCLUDE def_API('Z3_get_context_assignment', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_get_context_assignment(__in Z3_context c); + Z3_ast Z3_API Z3_get_context_assignment(Z3_context c); /*@}*/ #endif diff --git a/src/api/z3_fpa.h b/src/api/z3_fpa.h index e1bd67d66..b29534621 100644 --- a/src/api/z3_fpa.h +++ b/src/api/z3_fpa.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _Z3_FPA_H_ -#define _Z3_FPA_H_ +#ifndef Z3_FPA_H_ +#define Z3_FPA_H_ #ifdef __cplusplus extern "C" { @@ -42,7 +42,7 @@ extern "C" { def_API('Z3_mk_fpa_rounding_mode_sort', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_rounding_mode_sort(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_rounding_mode_sort(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. @@ -51,7 +51,7 @@ extern "C" { def_API('Z3_mk_fpa_round_nearest_ties_to_even', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToEven rounding mode. @@ -60,7 +60,7 @@ extern "C" { def_API('Z3_mk_fpa_rne', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_rne(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_rne(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. @@ -69,7 +69,7 @@ extern "C" { def_API('Z3_mk_fpa_round_nearest_ties_to_away', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the NearestTiesToAway rounding mode. @@ -78,7 +78,7 @@ extern "C" { def_API('Z3_mk_fpa_rna', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_rna(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_rna(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. @@ -87,7 +87,7 @@ extern "C" { def_API('Z3_mk_fpa_round_toward_positive', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardPositive rounding mode. @@ -96,7 +96,7 @@ extern "C" { def_API('Z3_mk_fpa_rtp', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_rtp(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_rtp(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. @@ -105,7 +105,7 @@ extern "C" { def_API('Z3_mk_fpa_round_toward_negative', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardNegative rounding mode. @@ -114,7 +114,7 @@ extern "C" { def_API('Z3_mk_fpa_rtn', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_rtn(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_rtn(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. @@ -123,7 +123,7 @@ extern "C" { def_API('Z3_mk_fpa_round_toward_zero', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(Z3_context c); /** \brief Create a numeral of RoundingMode sort which represents the TowardZero rounding mode. @@ -132,7 +132,7 @@ extern "C" { def_API('Z3_mk_fpa_rtz', AST, (_in(CONTEXT),)) */ - Z3_ast Z3_API Z3_mk_fpa_rtz(__in Z3_context c); + Z3_ast Z3_API Z3_mk_fpa_rtz(Z3_context c); /** \brief Create a FloatingPoint sort. @@ -145,7 +145,7 @@ extern "C" { def_API('Z3_mk_fpa_sort', SORT, (_in(CONTEXT), _in(UINT), _in(UINT))) */ - Z3_sort Z3_API Z3_mk_fpa_sort(__in Z3_context c, __in unsigned ebits, __in unsigned sbits); + Z3_sort Z3_API Z3_mk_fpa_sort(Z3_context c, unsigned ebits, unsigned sbits); /** \brief Create the half-precision (16-bit) FloatingPoint sort. @@ -154,7 +154,7 @@ extern "C" { def_API('Z3_mk_fpa_sort_half', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_sort_half(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_sort_half(Z3_context c); /** \brief Create the half-precision (16-bit) FloatingPoint sort. @@ -163,7 +163,7 @@ extern "C" { def_API('Z3_mk_fpa_sort_16', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_sort_16(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_sort_16(Z3_context c); /** \brief Create the single-precision (32-bit) FloatingPoint sort. @@ -172,7 +172,7 @@ extern "C" { def_API('Z3_mk_fpa_sort_single', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_sort_single(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_sort_single(Z3_context c); /** \brief Create the single-precision (32-bit) FloatingPoint sort. @@ -181,7 +181,7 @@ extern "C" { def_API('Z3_mk_fpa_sort_32', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_sort_32(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_sort_32(Z3_context c); /** \brief Create the double-precision (64-bit) FloatingPoint sort. @@ -190,7 +190,7 @@ extern "C" { def_API('Z3_mk_fpa_sort_double', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_sort_double(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_sort_double(Z3_context c); /** \brief Create the double-precision (64-bit) FloatingPoint sort. @@ -199,7 +199,7 @@ extern "C" { def_API('Z3_mk_fpa_sort_64', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_sort_64(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_sort_64(Z3_context c); /** \brief Create the quadruple-precision (128-bit) FloatingPoint sort. @@ -208,7 +208,7 @@ extern "C" { def_API('Z3_mk_fpa_sort_quadruple', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_sort_quadruple(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_sort_quadruple(Z3_context c); /** \brief Create the quadruple-precision (128-bit) FloatingPoint sort. @@ -217,7 +217,7 @@ extern "C" { def_API('Z3_mk_fpa_sort_128', SORT, (_in(CONTEXT),)) */ - Z3_sort Z3_API Z3_mk_fpa_sort_128(__in Z3_context c); + Z3_sort Z3_API Z3_mk_fpa_sort_128(Z3_context c); /** \brief Create a floating-point NaN of sort s. @@ -227,7 +227,7 @@ extern "C" { def_API('Z3_mk_fpa_nan', AST, (_in(CONTEXT),_in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_nan(__in Z3_context c, __in Z3_sort s); + Z3_ast Z3_API Z3_mk_fpa_nan(Z3_context c, Z3_sort s); /** \brief Create a floating-point infinity of sort s. @@ -240,7 +240,7 @@ extern "C" { def_API('Z3_mk_fpa_inf', AST, (_in(CONTEXT),_in(SORT),_in(BOOL))) */ - Z3_ast Z3_API Z3_mk_fpa_inf(__in Z3_context c, __in Z3_sort s, __in Z3_bool negative); + Z3_ast Z3_API Z3_mk_fpa_inf(Z3_context c, Z3_sort s, Z3_bool negative); /** \brief Create a floating-point zero of sort s. @@ -253,7 +253,7 @@ extern "C" { def_API('Z3_mk_fpa_zero', AST, (_in(CONTEXT),_in(SORT),_in(BOOL))) */ - Z3_ast Z3_API Z3_mk_fpa_zero(__in Z3_context c, __in Z3_sort s, __in Z3_bool negative); + Z3_ast Z3_API Z3_mk_fpa_zero(Z3_context c, Z3_sort s, Z3_bool negative); /** \brief Create an expression of FloatingPoint sort from three bit-vector expressions. @@ -271,7 +271,7 @@ extern "C" { def_API('Z3_mk_fpa_fp', AST, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_fp(__in Z3_context c, __in Z3_ast sgn, __in Z3_ast exp, __in Z3_ast sig); + Z3_ast Z3_API Z3_mk_fpa_fp(Z3_context c, Z3_ast sgn, Z3_ast exp, Z3_ast sig); /** \brief Create a numeral of FloatingPoint sort from a float. @@ -289,7 +289,7 @@ extern "C" { def_API('Z3_mk_fpa_numeral_float', AST, (_in(CONTEXT), _in(FLOAT), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_numeral_float(__in Z3_context c, __in float v, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_fpa_numeral_float(Z3_context c, float v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a double. @@ -307,7 +307,7 @@ extern "C" { def_API('Z3_mk_fpa_numeral_double', AST, (_in(CONTEXT), _in(DOUBLE), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_numeral_double(__in Z3_context c, __in double v, __in Z3_sort ty); + Z3_ast Z3_API Z3_mk_fpa_numeral_double(Z3_context c, double v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a signed integer. @@ -322,7 +322,7 @@ extern "C" { def_API('Z3_mk_fpa_numeral_int', AST, (_in(CONTEXT), _in(INT), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_numeral_int(__in Z3_context c, __in signed v, Z3_sort ty); + Z3_ast Z3_API Z3_mk_fpa_numeral_int(Z3_context c, signed v, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a sign bit and two integers. @@ -339,7 +339,7 @@ extern "C" { def_API('Z3_mk_fpa_numeral_int_uint', AST, (_in(CONTEXT), _in(BOOL), _in(INT), _in(UINT), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_numeral_int_uint(__in Z3_context c, __in Z3_bool sgn, __in signed exp, __in unsigned sig, Z3_sort ty); + Z3_ast Z3_API Z3_mk_fpa_numeral_int_uint(Z3_context c, Z3_bool sgn, signed exp, unsigned sig, Z3_sort ty); /** \brief Create a numeral of FloatingPoint sort from a sign bit and two 64-bit integers. @@ -356,7 +356,7 @@ extern "C" { def_API('Z3_mk_fpa_numeral_int64_uint64', AST, (_in(CONTEXT), _in(BOOL), _in(INT64), _in(UINT64), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(__in Z3_context c, __in Z3_bool sgn, __in __int64 exp, __in __uint64 sig, Z3_sort ty); + Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, Z3_bool sgn, __int64 exp, __uint64 sig, Z3_sort ty); /** \brief Floating-point absolute value @@ -366,7 +366,7 @@ extern "C" { def_API('Z3_mk_fpa_abs', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_abs(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_abs(Z3_context c, Z3_ast t); /** \brief Floating-point negation @@ -376,7 +376,7 @@ extern "C" { def_API('Z3_mk_fpa_neg', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_neg(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_neg(Z3_context c, Z3_ast t); /** \brief Floating-point addition @@ -390,7 +390,7 @@ extern "C" { def_API('Z3_mk_fpa_add', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_add(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_add(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point subtraction @@ -404,7 +404,7 @@ extern "C" { def_API('Z3_mk_fpa_sub', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_sub(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_sub(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point multiplication @@ -418,7 +418,7 @@ extern "C" { def_API('Z3_mk_fpa_mul', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_mul(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_mul(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point division @@ -432,7 +432,7 @@ extern "C" { def_API('Z3_mk_fpa_div', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_div(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_div(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2); /** \brief Floating-point fused multiply-add. @@ -449,7 +449,7 @@ extern "C" { def_API('Z3_mk_fpa_fma', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_fma(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t1, __in Z3_ast t2, __in Z3_ast t3); + Z3_ast Z3_API Z3_mk_fpa_fma(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2, Z3_ast t3); /** \brief Floating-point square root @@ -462,7 +462,7 @@ extern "C" { def_API('Z3_mk_fpa_sqrt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_sqrt(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_sqrt(Z3_context c, Z3_ast rm, Z3_ast t); /** \brief Floating-point remainder @@ -475,7 +475,7 @@ extern "C" { def_API('Z3_mk_fpa_rem', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_rem(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_rem(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point roundToIntegral. Rounds a floating-point number to @@ -489,7 +489,7 @@ extern "C" { def_API('Z3_mk_fpa_round_to_integral', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_round_to_integral(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_round_to_integral(Z3_context c, Z3_ast rm, Z3_ast t); /** \brief Minimum of floating-point numbers. @@ -502,7 +502,7 @@ extern "C" { def_API('Z3_mk_fpa_min', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_min(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_min(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Maximum of floating-point numbers. @@ -515,7 +515,7 @@ extern "C" { def_API('Z3_mk_fpa_max', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_max(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_max(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point less than or equal. @@ -528,7 +528,7 @@ extern "C" { def_API('Z3_mk_fpa_leq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_leq(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_leq(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point less than. @@ -541,7 +541,7 @@ extern "C" { def_API('Z3_mk_fpa_lt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_lt(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_lt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point greater than or equal. @@ -554,7 +554,7 @@ extern "C" { def_API('Z3_mk_fpa_geq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_geq(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_geq(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point greater than. @@ -567,7 +567,7 @@ extern "C" { def_API('Z3_mk_fpa_gt', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_gt(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_gt(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Floating-point equality. @@ -582,7 +582,7 @@ extern "C" { def_API('Z3_mk_fpa_eq', AST, (_in(CONTEXT),_in(AST),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_eq(__in Z3_context c, __in Z3_ast t1, __in Z3_ast t2); + Z3_ast Z3_API Z3_mk_fpa_eq(Z3_context c, Z3_ast t1, Z3_ast t2); /** \brief Predicate indicating whether t is a normal floating-point number. @@ -594,7 +594,7 @@ extern "C" { def_API('Z3_mk_fpa_is_normal', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_is_normal(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_is_normal(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a subnormal floating-point number. @@ -606,7 +606,7 @@ extern "C" { def_API('Z3_mk_fpa_is_subnormal', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_is_subnormal(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_is_subnormal(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a floating-point number with zero value, i.e., +zero or -zero. @@ -618,7 +618,7 @@ extern "C" { def_API('Z3_mk_fpa_is_zero', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_is_zero(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_is_zero(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a floating-point number representing +oo or -oo. @@ -630,7 +630,7 @@ extern "C" { def_API('Z3_mk_fpa_is_infinite', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_is_infinite(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_is_infinite(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a NaN. @@ -642,7 +642,7 @@ extern "C" { def_API('Z3_mk_fpa_is_nan', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_is_nan(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_is_nan(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a negative floating-point number. @@ -654,7 +654,7 @@ extern "C" { def_API('Z3_mk_fpa_is_negative', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_is_negative(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_is_negative(Z3_context c, Z3_ast t); /** \brief Predicate indicating whether t is a positive floating-point number. @@ -666,7 +666,7 @@ extern "C" { def_API('Z3_mk_fpa_is_positive', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_is_positive(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_is_positive(Z3_context c, Z3_ast t); /** \brief Conversion of a single IEEE 754-2008 bit-vector into a floating-point number. @@ -684,7 +684,7 @@ extern "C" { def_API('Z3_mk_fpa_to_fp_bv', AST, (_in(CONTEXT),_in(AST),_in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_to_fp_bv(__in Z3_context c, __in Z3_ast bv, __in Z3_sort s); + Z3_ast Z3_API Z3_mk_fpa_to_fp_bv(Z3_context c, Z3_ast bv, Z3_sort s); /** \brief Conversion of a FloatingPoint term into another term of different FloatingPoint sort. @@ -702,7 +702,7 @@ extern "C" { def_API('Z3_mk_fpa_to_fp_float', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_to_fp_float(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t, __in Z3_sort s); + Z3_ast Z3_API Z3_mk_fpa_to_fp_float(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief Conversion of a term of real sort into a term of FloatingPoint sort. @@ -720,7 +720,7 @@ extern "C" { def_API('Z3_mk_fpa_to_fp_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_to_fp_real(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t, __in Z3_sort s); + Z3_ast Z3_API Z3_mk_fpa_to_fp_real(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief Conversion of a 2's complement signed bit-vector term into a term of FloatingPoint sort. @@ -739,7 +739,7 @@ extern "C" { def_API('Z3_mk_fpa_to_fp_signed', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_to_fp_signed(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t, __in Z3_sort s); + Z3_ast Z3_API Z3_mk_fpa_to_fp_signed(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief Conversion of a 2's complement unsigned bit-vector term into a term of FloatingPoint sort. @@ -758,7 +758,7 @@ extern "C" { def_API('Z3_mk_fpa_to_fp_unsigned', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_to_fp_unsigned(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t, __in Z3_sort s); + Z3_ast Z3_API Z3_mk_fpa_to_fp_unsigned(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s); /** \brief Conversion of a floating-point term into an unsigned bit-vector. @@ -774,7 +774,7 @@ extern "C" { def_API('Z3_mk_fpa_to_ubv', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(UINT))) */ - Z3_ast Z3_API Z3_mk_fpa_to_ubv(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t, __in unsigned sz); + Z3_ast Z3_API Z3_mk_fpa_to_ubv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz); /** \brief Conversion of a floating-point term into a signed bit-vector. @@ -790,7 +790,7 @@ extern "C" { def_API('Z3_mk_fpa_to_sbv', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(UINT))) */ - Z3_ast Z3_API Z3_mk_fpa_to_sbv(__in Z3_context c, __in Z3_ast rm, __in Z3_ast t, __in unsigned sz); + Z3_ast Z3_API Z3_mk_fpa_to_sbv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz); /** \brief Conversion of a floating-point term into a real-numbered term. @@ -804,7 +804,7 @@ extern "C" { def_API('Z3_mk_fpa_to_real', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_to_real(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_to_real(Z3_context c, Z3_ast t); /** @@ -820,7 +820,7 @@ extern "C" { def_API('Z3_fpa_get_ebits', UINT, (_in(CONTEXT),_in(SORT))) */ - unsigned Z3_API Z3_fpa_get_ebits(__in Z3_context c, __in Z3_sort s); + unsigned Z3_API Z3_fpa_get_ebits(Z3_context c, Z3_sort s); /** \brief Retrieves the number of bits reserved for the significand in a FloatingPoint sort. @@ -830,7 +830,7 @@ extern "C" { def_API('Z3_fpa_get_sbits', UINT, (_in(CONTEXT),_in(SORT))) */ - unsigned Z3_API Z3_fpa_get_sbits(__in Z3_context c, __in Z3_sort s); + unsigned Z3_API Z3_fpa_get_sbits(Z3_context c, Z3_sort s); /** \brief Retrieves the sign of a floating-point literal. @@ -843,7 +843,7 @@ extern "C" { def_API('Z3_fpa_get_numeral_sign', BOOL, (_in(CONTEXT), _in(AST), _out(INT))) */ - Z3_bool Z3_API Z3_fpa_get_numeral_sign(__in Z3_context c, __in Z3_ast t, __out int * sgn); + Z3_bool Z3_API Z3_fpa_get_numeral_sign(Z3_context c, Z3_ast t, int * sgn); /** \brief Return the significand value of a floating-point numeral as a string. @@ -856,7 +856,21 @@ extern "C" { def_API('Z3_fpa_get_numeral_significand_string', STRING, (_in(CONTEXT), _in(AST))) */ - Z3_string Z3_API Z3_fpa_get_numeral_significand_string(__in Z3_context c, __in Z3_ast t); + Z3_string Z3_API Z3_fpa_get_numeral_significand_string(Z3_context c, Z3_ast t); + + /** + \brief Return the significand value of a floating-point numeral as a uint64. + + \param c logical context + \param t a floating-point numeral + + Remarks: This function extracts the significand bits in `t`, without the + hidden bit or normalization. Sets the Z3_INVALID_ARG error code if the + significand does not fit into a uint64. + + def_API('Z3_fpa_get_numeral_significand_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) + */ + Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, __uint64 * n); /** \brief Return the exponent value of a floating-point numeral as a string @@ -866,7 +880,7 @@ extern "C" { def_API('Z3_fpa_get_numeral_exponent_string', STRING, (_in(CONTEXT), _in(AST))) */ - Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(__in Z3_context c, __in Z3_ast t); + Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(Z3_context c, Z3_ast t); /** \brief Return the exponent value of a floating-point numeral as a signed 64-bit integer @@ -877,7 +891,7 @@ extern "C" { def_API('Z3_fpa_get_numeral_exponent_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64))) */ - Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(__in Z3_context c, __in Z3_ast t, __out __int64 * n); + Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n); /** \brief Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. @@ -894,7 +908,7 @@ extern "C" { def_API('Z3_mk_fpa_to_ieee_bv', AST, (_in(CONTEXT),_in(AST))) */ - Z3_ast Z3_API Z3_mk_fpa_to_ieee_bv(__in Z3_context c, __in Z3_ast t); + Z3_ast Z3_API Z3_mk_fpa_to_ieee_bv(Z3_context c, Z3_ast t); /** \brief Conversion of a real-sorted significand and an integer-sorted exponent into a term of FloatingPoint sort. @@ -913,7 +927,7 @@ extern "C" { def_API('Z3_mk_fpa_to_fp_int_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(__in Z3_context c, __in Z3_ast rm, __in Z3_ast exp, __in Z3_ast sig, __in Z3_sort s); + Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s); /*@}*/ diff --git a/src/api/z3_interp.h b/src/api/z3_interp.h index 6ae26c0ed..d775ff3ba 100644 --- a/src/api/z3_interp.h +++ b/src/api/z3_interp.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _Z3_INTERPOLATION_H_ -#define _Z3_INTERPOLATION_H_ +#ifndef Z3_INTERPOLATION_H_ +#define Z3_INTERPOLATION_H_ #ifdef __cplusplus extern "C" { @@ -43,7 +43,7 @@ extern "C" { def_API('Z3_mk_interpolant', AST, (_in(CONTEXT), _in(AST))) */ - Z3_ast Z3_API Z3_mk_interpolant(__in Z3_context c, __in Z3_ast a); + Z3_ast Z3_API Z3_mk_interpolant(Z3_context c, Z3_ast a); /** \brief This function generates a Z3 context suitable for generation of @@ -62,7 +62,7 @@ extern "C" { */ - Z3_context Z3_API Z3_mk_interpolation_context(__in Z3_config cfg); + Z3_context Z3_API Z3_mk_interpolation_context(Z3_config cfg); /** Compute an interpolant from a refutation. This takes a proof of "false" from a set of formulas C, and an interpolation @@ -130,7 +130,7 @@ extern "C" { def_API('Z3_get_interpolant', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(PARAMS))) */ - Z3_ast_vector Z3_API Z3_get_interpolant(__in Z3_context c, __in Z3_ast pf, __in Z3_ast pat, __in Z3_params p); + Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p); /* Compute an interpolant for an unsatisfiable conjunction of formulas. @@ -164,11 +164,11 @@ extern "C" { def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR), _out(MODEL))) */ - Z3_lbool Z3_API Z3_compute_interpolant(__in Z3_context c, - __in Z3_ast pat, - __in Z3_params p, - __out Z3_ast_vector *interp, - __out Z3_model *model); + Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, + Z3_ast pat, + Z3_params p, + Z3_ast_vector *interp, + Z3_model *model); /** Return a string summarizing cumulative time used for interpolation. This string is purely for entertainment purposes @@ -180,7 +180,7 @@ extern "C" { def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) */ - Z3_string Z3_API Z3_interpolation_profile(__in Z3_context ctx); + Z3_string Z3_API Z3_interpolation_profile(Z3_context ctx); /** \brief Read an interpolation problem from file. @@ -219,14 +219,14 @@ extern "C" { */ - int Z3_API Z3_read_interpolation_problem(__in Z3_context ctx, - __out unsigned *num, - __out Z3_ast *cnsts[], - __out unsigned *parents[], - __in Z3_string filename, - __out_opt Z3_string_ptr error, - __out unsigned *num_theory, - __out Z3_ast *theory[]); + int Z3_API Z3_read_interpolation_problem(Z3_context ctx, + unsigned *num, + Z3_ast *cnsts[], + unsigned *parents[], + Z3_string filename, + Z3_string_ptr error, + unsigned *num_theory, + Z3_ast *theory[]); @@ -250,14 +250,14 @@ extern "C" { def_API('Z3_check_interpolant', INT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in_array(1, AST), _out(STRING), _in(UINT), _in_array(6, AST))) */ - int Z3_API Z3_check_interpolant(__in Z3_context ctx, - __in unsigned num, - __in_ecount(num) Z3_ast cnsts[], - __in_ecount(num) unsigned parents[], - __in_ecount(num - 1) Z3_ast *interps, - __out_opt Z3_string_ptr error, - __in unsigned num_theory, - __in_ecount(num_theory) Z3_ast theory[]); + int Z3_API Z3_check_interpolant(Z3_context ctx, + unsigned num, + Z3_ast cnsts[], + unsigned parents[], + Z3_ast *interps, + Z3_string_ptr error, + unsigned num_theory, + Z3_ast theory[]); /** Write an interpolation problem to file suitable for reading with Z3_read_interpolation_problem. The output file is a sequence @@ -275,13 +275,13 @@ extern "C" { def_API('Z3_write_interpolation_problem', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(STRING), _in(UINT), _in_array(5, AST))) */ - void Z3_API Z3_write_interpolation_problem(__in Z3_context ctx, - __in unsigned num, - __in_ecount(num) Z3_ast cnsts[], - __in_ecount(num) unsigned parents[], - __in Z3_string filename, - __in unsigned num_theory, - __in_ecount(num_theory) Z3_ast theory[]); + void Z3_API Z3_write_interpolation_problem(Z3_context ctx, + unsigned num, + Z3_ast cnsts[], + unsigned parents[], + Z3_string filename, + unsigned num_theory, + Z3_ast theory[]); /*@}*/ /*@}*/ diff --git a/src/api/z3_macros.h b/src/api/z3_macros.h index 7a0b6857c..cb3cc3a98 100644 --- a/src/api/z3_macros.h +++ b/src/api/z3_macros.h @@ -1,34 +1,8 @@ -#ifndef __in -#define __in -#endif -#ifndef __out -#define __out -#endif +/*++ +Copyright (c) 2015 Microsoft Corporation -#ifndef __out_opt -#define __out_opt __out -#endif - -#ifndef __ecount -#define __ecount(num_args) -#endif - -#ifndef __in_ecount -#define __in_ecount(num_args) __in __ecount(num_args) -#endif - -#ifndef __out_ecount -#define __out_ecount(num_args) __out __ecount(num_args) -#endif - -#ifndef __inout_ecount -#define __inout_ecount(num_args) __in __out __ecount(num_args) -#endif - -#ifndef __inout -#define __inout __in __out -#endif +--*/ #ifndef Z3_bool_opt #define Z3_bool_opt Z3_bool diff --git a/src/api/z3_polynomial.h b/src/api/z3_polynomial.h index f55a63dd4..f3e40b3b2 100644 --- a/src/api/z3_polynomial.h +++ b/src/api/z3_polynomial.h @@ -17,8 +17,8 @@ Notes: --*/ -#ifndef _Z3_POLYNOMIAL_H_ -#define _Z3_POLYNOMIAL_H_ +#ifndef Z3_POLYNOMIAL_H_ +#define Z3_POLYNOMIAL_H_ #ifdef __cplusplus extern "C" { @@ -48,7 +48,7 @@ extern "C" { def_API('Z3_polynomial_subresultants', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ - Z3_ast_vector Z3_API Z3_polynomial_subresultants(__in Z3_context c, __in Z3_ast p, __in Z3_ast q, __in Z3_ast x); + Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x); /*@}*/ diff --git a/src/api/z3_private.h b/src/api/z3_private.h index 91becd158..a404f7b09 100644 --- a/src/api/z3_private.h +++ b/src/api/z3_private.h @@ -22,8 +22,8 @@ Notes: #include"rational.h" #include"z3_macros.h" -#ifndef _Z3_PRIVATE__H_ -#define _Z3_PRIVATE__H_ +#ifndef Z3_PRIVATE_H_ +#define Z3_PRIVATE_H_ #ifndef CAMLIDL @@ -34,7 +34,7 @@ extern "C" { [pointer_default(ref)] interface Z3 { #endif // CAMLIDL - Z3_bool Z3_API Z3_get_numeral_rational(__in Z3_context c, __in Z3_ast a, rational& r); + Z3_bool Z3_API Z3_get_numeral_rational(Z3_context c, Z3_ast a, rational& r); #ifndef CAMLIDL #ifdef __cplusplus diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index 04fe40253..10903750f 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -19,8 +19,8 @@ Author: Notes: --*/ -#ifndef _Z3_RCF_H_ -#define _Z3_RCF_H_ +#ifndef Z3_RCF_H_ +#define Z3_RCF_H_ #ifdef __cplusplus extern "C" { @@ -43,42 +43,42 @@ extern "C" { def_API('Z3_rcf_del', VOID, (_in(CONTEXT), _in(RCF_NUM))) */ - void Z3_API Z3_rcf_del(__in Z3_context c, __in Z3_rcf_num a); + void Z3_API Z3_rcf_del(Z3_context c, Z3_rcf_num a); /** \brief Return a RCF rational using the given string. def_API('Z3_rcf_mk_rational', RCF_NUM, (_in(CONTEXT), _in(STRING))) */ - Z3_rcf_num Z3_API Z3_rcf_mk_rational(__in Z3_context c, __in Z3_string val); + Z3_rcf_num Z3_API Z3_rcf_mk_rational(Z3_context c, Z3_string val); /** \brief Return a RCF small integer. def_API('Z3_rcf_mk_small_int', RCF_NUM, (_in(CONTEXT), _in(INT))) */ - Z3_rcf_num Z3_API Z3_rcf_mk_small_int(__in Z3_context c, __in int val); + Z3_rcf_num Z3_API Z3_rcf_mk_small_int(Z3_context c, int val); /** \brief Return Pi def_API('Z3_rcf_mk_pi', RCF_NUM, (_in(CONTEXT),)) */ - Z3_rcf_num Z3_API Z3_rcf_mk_pi(__in Z3_context c); + Z3_rcf_num Z3_API Z3_rcf_mk_pi(Z3_context c); /** \brief Return e (Euler's constant) def_API('Z3_rcf_mk_e', RCF_NUM, (_in(CONTEXT),)) */ - Z3_rcf_num Z3_API Z3_rcf_mk_e(__in Z3_context c); + Z3_rcf_num Z3_API Z3_rcf_mk_e(Z3_context c); /** \brief Return a new infinitesimal that is smaller than all elements in the Z3 field. def_API('Z3_rcf_mk_infinitesimal', RCF_NUM, (_in(CONTEXT),)) */ - Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(__in Z3_context c); + Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(Z3_context c); /** \brief Store in roots the roots of the polynomial a[n-1]*x^{n-1} + ... + a[0]. @@ -89,112 +89,112 @@ extern "C" { def_API('Z3_rcf_mk_roots', UINT, (_in(CONTEXT), _in(UINT), _in_array(1, RCF_NUM), _out_array(1, RCF_NUM))) */ - unsigned Z3_API Z3_rcf_mk_roots(__in Z3_context c, __in unsigned n, __in_ecount(n) Z3_rcf_num const a[], __out_ecount(n) Z3_rcf_num roots[]); + unsigned Z3_API Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]); /** \brief Return the value a + b. def_API('Z3_rcf_add', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_rcf_num Z3_API Z3_rcf_add(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_rcf_num Z3_API Z3_rcf_add(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return the value a - b. def_API('Z3_rcf_sub', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_rcf_num Z3_API Z3_rcf_sub(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_rcf_num Z3_API Z3_rcf_sub(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return the value a * b. def_API('Z3_rcf_mul', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_rcf_num Z3_API Z3_rcf_mul(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_rcf_num Z3_API Z3_rcf_mul(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return the value a / b. def_API('Z3_rcf_div', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_rcf_num Z3_API Z3_rcf_div(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_rcf_num Z3_API Z3_rcf_div(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return the value -a def_API('Z3_rcf_neg', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) */ - Z3_rcf_num Z3_API Z3_rcf_neg(__in Z3_context c, __in Z3_rcf_num a); + Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a); /** \brief Return the value 1/a def_API('Z3_rcf_inv', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) */ - Z3_rcf_num Z3_API Z3_rcf_inv(__in Z3_context c, __in Z3_rcf_num a); + Z3_rcf_num Z3_API Z3_rcf_inv(Z3_context c, Z3_rcf_num a); /** \brief Return the value a^k def_API('Z3_rcf_power', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) */ - Z3_rcf_num Z3_API Z3_rcf_power(__in Z3_context c, __in Z3_rcf_num a, __in unsigned k); + Z3_rcf_num Z3_API Z3_rcf_power(Z3_context c, Z3_rcf_num a, unsigned k); /** \brief Return Z3_TRUE if a < b def_API('Z3_rcf_lt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_bool Z3_API Z3_rcf_lt(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_bool Z3_API Z3_rcf_lt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a > b def_API('Z3_rcf_gt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_bool Z3_API Z3_rcf_gt(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_bool Z3_API Z3_rcf_gt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a <= b def_API('Z3_rcf_le', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_bool Z3_API Z3_rcf_le(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_bool Z3_API Z3_rcf_le(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a >= b def_API('Z3_rcf_ge', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_bool Z3_API Z3_rcf_ge(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_bool Z3_API Z3_rcf_ge(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a == b def_API('Z3_rcf_eq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_bool Z3_API Z3_rcf_eq(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_bool Z3_API Z3_rcf_eq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Return Z3_TRUE if a != b def_API('Z3_rcf_neq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) */ - Z3_bool Z3_API Z3_rcf_neq(__in Z3_context c, __in Z3_rcf_num a, __in Z3_rcf_num b); + Z3_bool Z3_API Z3_rcf_neq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); /** \brief Convert the RCF numeral into a string. def_API('Z3_rcf_num_to_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(BOOL), _in(BOOL))) */ - Z3_string Z3_API Z3_rcf_num_to_string(__in Z3_context c, __in Z3_rcf_num a, __in Z3_bool compact, __in Z3_bool html); + Z3_string Z3_API Z3_rcf_num_to_string(Z3_context c, Z3_rcf_num a, Z3_bool compact, Z3_bool html); /** \brief Convert the RCF numeral into a string in decimal notation. def_API('Z3_rcf_num_to_decimal_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) */ - Z3_string Z3_API Z3_rcf_num_to_decimal_string(__in Z3_context c, __in Z3_rcf_num a, __in unsigned prec); + Z3_string Z3_API Z3_rcf_num_to_decimal_string(Z3_context c, Z3_rcf_num a, unsigned prec); /** \brief Extract the "numerator" and "denominator" of the given RCF numeral. @@ -202,7 +202,7 @@ extern "C" { def_API('Z3_rcf_get_numerator_denominator', VOID, (_in(CONTEXT), _in(RCF_NUM), _out(RCF_NUM), _out(RCF_NUM))) */ - void Z3_API Z3_rcf_get_numerator_denominator(__in Z3_context c, __in Z3_rcf_num a, __out Z3_rcf_num * n, __out Z3_rcf_num * d); + void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d); /*@}*/ /*@}*/ diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index cfd022124..927521b65 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -22,12 +22,14 @@ Notes: #include"stream_buffer.h" #include"symbol.h" #include"trace.h" +#include void register_z3_replayer_cmds(z3_replayer & in); + void throw_invalid_reference() { TRACE("z3_replayer", tout << "invalid argument reference\n";); - throw z3_replayer_exception("invalid argument reference"); + throw z3_replayer_exception("invalid argument reference1"); } struct z3_replayer::imp { @@ -45,7 +47,38 @@ struct z3_replayer::imp { size_t_map m_heap; svector m_cmds; - enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT }; + enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, INT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT }; + + char const* kind2string(value_kind k) const { + switch (k) { + case INT64: return "int64"; + case UINT64: return "uint64"; + case DOUBLE: return "double"; + case STRING: return "string"; + case SYMBOL: return "symbol"; + case OBJECT: return "object"; + case UINT_ARRAY: return "uint_array"; + case INT_ARRAY: return "int_array"; + case SYMBOL_ARRAY: return "symbol_array"; + case OBJECT_ARRAY: return "object_array"; + case FLOAT: return "float"; + default: UNREACHABLE(); return "unknown"; + } + } + + + void check_arg(unsigned pos, value_kind k) const { + if (pos >= m_args.size()) { + TRACE("z3_replayer", tout << "too few arguments " << m_args.size() << " expecting " << kind2string(k) << "\n";); + throw z3_replayer_exception("invalid argument reference2"); + } + if (m_args[pos].m_kind != k) { + std::stringstream strm; + strm << "expecting " << kind2string(k) << " at position " + << pos << " but got " << kind2string(m_args[pos].m_kind); + throw z3_replayer_exception(strm.str().c_str()); + } + } struct value { value_kind m_kind; @@ -71,6 +104,7 @@ struct z3_replayer::imp { vector > m_obj_arrays; vector > m_sym_arrays; vector m_unsigned_arrays; + vector > m_int_arrays; imp(z3_replayer & o, std::istream & in): m_owner(o), @@ -321,6 +355,15 @@ struct z3_replayer::imp { v.push_back(static_cast(m_args[i].m_uint)); } } + if (k == INT64) { + aidx = m_int_arrays.size(); + nk = INT_ARRAY; + m_int_arrays.push_back(svector()); + svector & v = m_int_arrays.back(); + for (unsigned i = asz - sz; i < asz; i++) { + v.push_back(static_cast(m_args[i].m_int)); + } + } else if (k == SYMBOL) { aidx = m_sym_arrays.size(); nk = SYMBOL_ARRAY; @@ -489,8 +532,7 @@ struct z3_replayer::imp { next(); skip_blank(); read_ptr(); skip_blank(); read_uint64(); unsigned pos = static_cast(m_uint64); TRACE("z3_replayer", tout << "[" << m_line << "] " << "* " << m_ptr << " " << pos << "\n";); - if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT) - throw_invalid_reference(); + check_arg(pos, OBJECT); m_heap.insert(m_ptr, m_args[pos].m_obj); break; } @@ -499,8 +541,7 @@ struct z3_replayer::imp { // @ obj_id array_pos idx next(); skip_blank(); read_ptr(); skip_blank(); read_uint64(); unsigned pos = static_cast(m_uint64); - if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT_ARRAY) - throw_invalid_reference(); + check_arg(pos, OBJECT_ARRAY); unsigned aidx = static_cast(m_args[pos].m_uint); ptr_vector & v = m_obj_arrays[aidx]; skip_blank(); read_uint64(); @@ -525,26 +566,22 @@ struct z3_replayer::imp { } int get_int(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != INT64) - throw_invalid_reference(); + check_arg(pos, INT64); return static_cast(m_args[pos].m_int); } __int64 get_int64(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != INT64) - throw_invalid_reference(); + check_arg(pos, INT64); return m_args[pos].m_int; } unsigned get_uint(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != UINT64) - throw_invalid_reference(); + check_arg(pos, UINT64); return static_cast(m_args[pos].m_uint); } __uint64 get_uint64(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != UINT64) - throw_invalid_reference(); + check_arg(pos, UINT64); return m_args[pos].m_uint; } @@ -555,46 +592,45 @@ struct z3_replayer::imp { } double get_double(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != DOUBLE) - throw_invalid_reference(); + check_arg(pos, DOUBLE); return m_args[pos].m_double; } Z3_string get_str(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != STRING) - throw_invalid_reference(); + check_arg(pos, STRING); return m_args[pos].m_str; } Z3_symbol get_symbol(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != SYMBOL) - throw_invalid_reference(); + check_arg(pos, SYMBOL); return reinterpret_cast(const_cast(m_args[pos].m_str)); } void * get_obj(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT) - throw_invalid_reference(); + check_arg(pos, OBJECT); return m_args[pos].m_obj; } unsigned * get_uint_array(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != UINT_ARRAY) - throw_invalid_reference(); + check_arg(pos, UINT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return m_unsigned_arrays[idx].c_ptr(); } + int * get_int_array(unsigned pos) const { + check_arg(pos, INT_ARRAY); + unsigned idx = static_cast(m_args[pos].m_uint); + return m_int_arrays[idx].c_ptr(); + } + Z3_symbol * get_symbol_array(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != SYMBOL_ARRAY) - throw_invalid_reference(); + check_arg(pos, SYMBOL_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); return m_sym_arrays[idx].c_ptr(); } void ** get_obj_array(unsigned pos) const { - if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT_ARRAY) - throw_invalid_reference(); + check_arg(pos, OBJECT_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); ptr_vector const & v = m_obj_arrays[idx]; TRACE("z3_replayer_bug", tout << "pos: " << pos << ", idx: " << idx << " size(): " << v.size() << "\n"; @@ -603,38 +639,32 @@ struct z3_replayer::imp { } int * get_int_addr(unsigned pos) { - if (pos >= m_args.size() || m_args[pos].m_kind != INT64) - throw_invalid_reference(); + check_arg(pos, INT64); return reinterpret_cast(&(m_args[pos].m_int)); } __int64 * get_int64_addr(unsigned pos) { - if (pos >= m_args.size() || m_args[pos].m_kind != INT64) - throw_invalid_reference(); + check_arg(pos, INT64); return &(m_args[pos].m_int); } unsigned * get_uint_addr(unsigned pos) { - if (pos >= m_args.size() || m_args[pos].m_kind != UINT64) - throw_invalid_reference(); + check_arg(pos, UINT64); return reinterpret_cast(&(m_args[pos].m_uint)); } __uint64 * get_uint64_addr(unsigned pos) { - if (pos >= m_args.size() || m_args[pos].m_kind != UINT64) - throw_invalid_reference(); + check_arg(pos, UINT64); return &(m_args[pos].m_uint); } Z3_string * get_str_addr(unsigned pos) { - if (pos >= m_args.size() || m_args[pos].m_kind != STRING) - throw_invalid_reference(); + check_arg(pos, STRING); return &(m_args[pos].m_str); } void ** get_obj_addr(unsigned pos) { - if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT) - throw_invalid_reference(); + check_arg(pos, OBJECT); return &(m_args[pos].m_obj); } @@ -653,6 +683,7 @@ struct z3_replayer::imp { m_obj_arrays.reset(); m_sym_arrays.reset(); m_unsigned_arrays.reset(); + m_int_arrays.reset(); } @@ -715,6 +746,10 @@ unsigned * z3_replayer::get_uint_array(unsigned pos) const { return m_imp->get_uint_array(pos); } +int * z3_replayer::get_int_array(unsigned pos) const { + return m_imp->get_int_array(pos); +} + Z3_symbol * z3_replayer::get_symbol_array(unsigned pos) const { return m_imp->get_symbol_array(pos); } diff --git a/src/api/z3_replayer.h b/src/api/z3_replayer.h index 6320068ad..6c566d553 100644 --- a/src/api/z3_replayer.h +++ b/src/api/z3_replayer.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _Z3_REPLAYER_H_ -#define _Z3_REPLAYER_H_ +#ifndef Z3_REPLAYER_H_ +#define Z3_REPLAYER_H_ #include #include"z3.h" @@ -50,6 +50,7 @@ public: void * get_obj(unsigned pos) const; unsigned * get_uint_array(unsigned pos) const; + int * get_int_array(unsigned pos) const; Z3_symbol * get_symbol_array(unsigned pos) const; void ** get_obj_array(unsigned pos) const; diff --git a/src/api/z3_v1.h b/src/api/z3_v1.h index 2c12fa610..11f492d88 100644 --- a/src/api/z3_v1.h +++ b/src/api/z3_v1.h @@ -18,8 +18,8 @@ Author: Notes: --*/ -#ifndef _Z3_V1_H_ -#define _Z3_V1_H_ +#ifndef Z3_V1_H_ +#define Z3_V1_H_ #include"z3.h" diff --git a/src/ast/act_cache.h b/src/ast/act_cache.h index 0a95f0f40..b3567dec2 100644 --- a/src/ast/act_cache.h +++ b/src/ast/act_cache.h @@ -17,8 +17,8 @@ Author: Notes: --*/ -#ifndef _ACT_CACHE_H_ -#define _ACT_CACHE_H_ +#ifndef ACT_CACHE_H_ +#define ACT_CACHE_H_ #include"ast.h" #include"obj_hashtable.h" diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index cd33c7782..cfb6f5469 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ARITH_DECL_PLUGIN_H_ -#define _ARITH_DECL_PLUGIN_H_ +#ifndef ARITH_DECL_PLUGIN_H_ +#define ARITH_DECL_PLUGIN_H_ #include"ast.h" class sexpr; @@ -399,5 +399,5 @@ public: } }; -#endif /* _ARITH_DECL_PLUGIN_H_ */ +#endif /* ARITH_DECL_PLUGIN_H_ */ diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 2184b0088..98fd563f0 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ARRAY_DECL_PLUGIN_H_ -#define _ARRAY_DECL_PLUGIN_H_ +#ifndef ARRAY_DECL_PLUGIN_H_ +#define ARRAY_DECL_PLUGIN_H_ #include"ast.h" @@ -187,5 +187,5 @@ public: }; -#endif /* _ARRAY_DECL_PLUGIN_H_ */ +#endif /* ARRAY_DECL_PLUGIN_H_ */ diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 3a6275e33..840222e89 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -316,7 +316,8 @@ func_decl::func_decl(symbol const & name, unsigned arity, sort * const * domain, decl(AST_FUNC_DECL, name, info), m_arity(arity), m_range(range) { - memcpy(const_cast(get_domain()), domain, sizeof(sort *) * arity); + if (arity != 0) + memcpy(const_cast(get_domain()), domain, sizeof(sort *) * arity); } // ----------------------------------- @@ -378,8 +379,10 @@ quantifier::quantifier(bool forall, unsigned num_decls, sort * const * decl_sort memcpy(const_cast(get_decl_sorts()), decl_sorts, sizeof(sort *) * num_decls); memcpy(const_cast(get_decl_names()), decl_names, sizeof(symbol) * num_decls); - memcpy(const_cast(get_patterns()), patterns, sizeof(expr *) * num_patterns); - memcpy(const_cast(get_no_patterns()), no_patterns, sizeof(expr *) * num_no_patterns); + if (num_patterns != 0) + memcpy(const_cast(get_patterns()), patterns, sizeof(expr *) * num_patterns); + if (num_no_patterns != 0) + memcpy(const_cast(get_no_patterns()), no_patterns, sizeof(expr *) * num_no_patterns); } // ----------------------------------- @@ -1013,17 +1016,43 @@ func_decl * basic_decl_plugin::mk_ite_decl(sort * s) { return m_ite_decls[id]; } +sort* basic_decl_plugin::join(unsigned n, sort* const* srts) { + SASSERT(n > 0); + sort* s = srts[0]; + while (n > 1) { + ++srts; + --n; + s = join(s, *srts); + } + return s; +} + +sort* basic_decl_plugin::join(unsigned n, expr* const* es) { + SASSERT(n > 0); + sort* s = m_manager->get_sort(*es); + while (n > 1) { + ++es; + --n; + s = join(s, m_manager->get_sort(*es)); + } + return s; +} + sort* basic_decl_plugin::join(sort* s1, sort* s2) { if (s1 == s2) return s1; - if (s1->get_family_id() == m_manager->m_arith_family_id && + if (s1->get_family_id() == m_manager->m_arith_family_id && s2->get_family_id() == m_manager->m_arith_family_id) { if (s1->get_decl_kind() == REAL_SORT) { return s1; } + return s2; } - return s2; + std::ostringstream buffer; + buffer << "Sorts " << mk_pp(s1, *m_manager) << " and " << mk_pp(s2, *m_manager) << " are incompatible"; + throw ast_exception(buffer.str().c_str()); } + func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { switch (static_cast(k)) { @@ -1038,8 +1067,8 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_XOR: return m_xor_decl; case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : 0; // eq and oeq must have at least two arguments, they can have more since they are chainable - case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(domain[0],domain[1]), m_eq_decls) : 0; - case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(domain[0],domain[1]), m_oeq_decls) : 0; + case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(arity, domain), m_eq_decls) : 0; + case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(arity, domain), m_oeq_decls) : 0; case OP_DISTINCT: { func_decl_info info(m_family_id, OP_DISTINCT); info.set_pairwise(); @@ -1081,10 +1110,8 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_XOR: return m_xor_decl; case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): 0; // eq and oeq must have at least two arguments, they can have more since they are chainable - case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, join(m_manager->get_sort(args[0]), - m_manager->get_sort(args[1])), m_eq_decls) : 0; - case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(m_manager->get_sort(args[0]), - m_manager->get_sort(args[1])), m_oeq_decls) : 0; + case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, join(num_args, args), m_eq_decls) : 0; + case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(num_args, args), m_oeq_decls) : 0; case OP_DISTINCT: return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); default: @@ -1975,75 +2002,114 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co } app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const * args) { - unsigned sz = app::get_obj_size(num_args); - void * mem = allocate_node(sz); - app * new_node; - app * r; - if (m_int_real_coercions && coercion_needed(decl, num_args, args)) { - expr_ref_buffer new_args(*this); - if (decl->is_associative()) { - sort * d = decl->get_domain(0); - for (unsigned i = 0; i < num_args; i++) { - sort * s = get_sort(args[i]); - if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) { - if (d->get_decl_kind() == REAL_SORT) - new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i])); - else - new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i])); - } - else { - new_args.push_back(args[i]); + app * r = 0; + app * new_node = 0; + unsigned sz = app::get_obj_size(num_args); + void * mem = allocate_node(sz); + + try { + if (m_int_real_coercions && coercion_needed(decl, num_args, args)) { + expr_ref_buffer new_args(*this); + if (decl->is_associative()) { + sort * d = decl->get_domain(0); + for (unsigned i = 0; i < num_args; i++) { + sort * s = get_sort(args[i]); + if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) { + if (d->get_decl_kind() == REAL_SORT) + new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i])); + else + new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i])); + } + else { + new_args.push_back(args[i]); + } } } + else { + for (unsigned i = 0; i < num_args; i++) { + sort * d = decl->get_domain(i); + sort * s = get_sort(args[i]); + if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) { + if (d->get_decl_kind() == REAL_SORT) + new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i])); + else + new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i])); + } + else { + new_args.push_back(args[i]); + } + } + } + check_args(decl, num_args, new_args.c_ptr()); + SASSERT(new_args.size() == num_args); + new_node = new (mem)app(decl, num_args, new_args.c_ptr()); + r = register_node(new_node); } else { - for (unsigned i = 0; i < num_args; i++) { - sort * d = decl->get_domain(i); - sort * s = get_sort(args[i]); - if (d != s && d->get_family_id() == m_arith_family_id && s->get_family_id() == m_arith_family_id) { - if (d->get_decl_kind() == REAL_SORT) - new_args.push_back(mk_app(m_arith_family_id, OP_TO_REAL, args[i])); - else - new_args.push_back(mk_app(m_arith_family_id, OP_TO_INT, args[i])); - } - else { - new_args.push_back(args[i]); - } + check_args(decl, num_args, args); + new_node = new (mem)app(decl, num_args, args); + r = register_node(new_node); + } + + if (m_trace_stream && r == new_node) { + *m_trace_stream << "[mk-app] #" << r->get_id() << " "; + if (r->get_num_args() == 0 && r->get_decl()->get_name() == "int") { + ast_ll_pp(*m_trace_stream, *this, r); + } + else if (is_label_lit(r)) { + ast_ll_pp(*m_trace_stream, *this, r); + } + else { + *m_trace_stream << r->get_decl()->get_name(); + for (unsigned i = 0; i < r->get_num_args(); i++) + *m_trace_stream << " #" << r->get_arg(i)->get_id(); + *m_trace_stream << "\n"; } } - SASSERT(new_args.size() == num_args); - new_node = new (mem) app(decl, num_args, new_args.c_ptr()); - r = register_node(new_node); } - else { - new_node = new (mem) app(decl, num_args, args); - r = register_node(new_node); - } - - if (m_trace_stream && r == new_node) { - *m_trace_stream << "[mk-app] #" << r->get_id() << " "; - if (r->get_num_args() == 0 && r->get_decl()->get_name() == "int") { - ast_ll_pp(*m_trace_stream, *this, r); - } else if (is_label_lit(r)) { - ast_ll_pp(*m_trace_stream, *this, r); - } else { - *m_trace_stream << r->get_decl()->get_name(); - for (unsigned i = 0; i < r->get_num_args(); i++) - *m_trace_stream << " #" << r->get_arg(i)->get_id(); - *m_trace_stream << "\n"; - } + catch (...) { + deallocate_node(static_cast(mem), sz); + throw; } return r; } +void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) { + for (unsigned i = 0; i < n; i++) { + sort * actual_sort = get_sort(es[i]); + sort * expected_sort = f->is_associative() ? f->get_domain(0) : f->get_domain(i); + if (expected_sort != actual_sort) { + std::ostringstream buffer; + buffer << "Sort mismatch at argument #" << (i+1) + << " for function " << mk_pp(f,*this) + << " supplied sort is " + << mk_pp(actual_sort, *this); + throw ast_exception(buffer.str().c_str()); + } + } +} + + inline app * ast_manager::mk_app_core(func_decl * decl, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_app_core(decl, 2, args); } app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * args) { - SASSERT(decl->get_arity() == num_args || decl->is_right_associative() || decl->is_left_associative() || decl->is_chainable()); + bool type_error = + decl->get_arity() != num_args && !decl->is_right_associative() && + !decl->is_left_associative() && !decl->is_chainable(); + + type_error |= (decl->get_arity() != num_args && num_args < 2 && + decl->get_family_id() == m_basic_family_id && !decl->is_associative()); + + if (type_error) { + std::ostringstream buffer; + buffer << "Wrong number of arguments (" << num_args + << ") passed to function " << mk_pp(decl, *this); + throw ast_exception(buffer.str().c_str()); + } app * r = 0; if (num_args > 2 && !decl->is_flat_associative()) { if (decl->is_right_associative()) { @@ -2078,6 +2144,8 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar return r; } + + func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range) { func_decl_info info(null_family_id, null_decl_kind); @@ -2574,6 +2642,8 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n"; tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";); SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() || + ((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) && + (is_iff(get_fact(p2)) || is_eq(get_fact(p2)))) || ( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) && (is_eq(get_fact(p2)) || is_oeq(get_fact(p2))))); CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), diff --git a/src/ast/ast.h b/src/ast/ast.h index a5f5c286f..deddd3cad 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _AST_H_ -#define _AST_H_ +#ifndef AST_H_ +#define AST_H_ #include"vector.h" #include"hashtable.h" @@ -44,6 +44,7 @@ Revision History: #include"chashtable.h" #include"z3_exception.h" #include"dependency.h" +#include"rlimit.h" #define RECYCLE_FREE_AST_INDICES @@ -1101,6 +1102,8 @@ protected: func_decl * mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector & cache); func_decl * mk_ite_decl(sort * s); sort* join(sort* s1, sort* s2); + sort* join(unsigned n, sort*const* srts); + sort* join(unsigned n, expr*const* es); public: basic_decl_plugin(); @@ -1422,6 +1425,7 @@ public: void show_id_gen(); protected: + reslimit m_limit; small_object_allocator m_alloc; family_manager m_family_manager; expr_array_manager m_expr_array_manager; @@ -1458,6 +1462,9 @@ protected: bool coercion_needed(func_decl * decl, unsigned num_args, expr * const * args); + void check_args(func_decl* f, unsigned n, expr* const* es); + + public: ast_manager(proof_gen_mode = PGM_DISABLED, char const * trace_file = 0, bool is_format_manager = false); ast_manager(proof_gen_mode, std::fstream * trace_stream, bool is_format_manager = false); @@ -1514,6 +1521,8 @@ public: fid == m_user_sort_family_id; } + reslimit& limit() { return m_limit; } + void register_plugin(symbol const & s, decl_plugin * plugin); void register_plugin(family_id id, decl_plugin * plugin); @@ -2006,6 +2015,7 @@ public: app * mk_false() { return m_false; } app * mk_interp(expr * arg) { return mk_app(m_basic_family_id, OP_INTERP, arg); } + func_decl* mk_and_decl() { sort* domain[2] = { m_bool_sort, m_bool_sort }; return mk_func_decl(m_basic_family_id, OP_AND, 0, 0, 2, domain); @@ -2449,6 +2459,6 @@ public: void operator()(AST * n) { m_manager.inc_ref(n); } }; -#endif /* _AST_H_ */ +#endif /* AST_H_ */ diff --git a/src/ast/ast_ll_pp.h b/src/ast/ast_ll_pp.h index 2cfe83179..7fc527833 100644 --- a/src/ast/ast_ll_pp.h +++ b/src/ast/ast_ll_pp.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _AST_LL_PP_H_ -#define _AST_LL_PP_H_ +#ifndef AST_LL_PP_H_ +#define AST_LL_PP_H_ #include"ast.h" #include @@ -53,5 +53,5 @@ inline std::ostream & operator<<(std::ostream & out, mk_bounded_pp const & p) { } -#endif /* _AST_LL_PP_H_ */ +#endif /* AST_LL_PP_H_ */ diff --git a/src/ast/ast_lt.h b/src/ast/ast_lt.h index b405ab229..a5899ba9a 100644 --- a/src/ast/ast_lt.h +++ b/src/ast/ast_lt.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _AST_LT_H_ -#define _AST_LT_H_ +#ifndef AST_LT_H_ +#define AST_LT_H_ class ast; diff --git a/src/ast/ast_pp.h b/src/ast/ast_pp.h index 7e7bdfc4e..ecd7e3b13 100644 --- a/src/ast/ast_pp.h +++ b/src/ast/ast_pp.h @@ -18,8 +18,8 @@ Revision History: 2012-11-17 - ast_smt2_pp is the official pretty printer in Z3 --*/ -#ifndef _AST_PP_H_ -#define _AST_PP_H_ +#ifndef AST_PP_H_ +#define AST_PP_H_ #include"ast_smt2_pp.h" diff --git a/src/ast/ast_pp_util.cpp b/src/ast/ast_pp_util.cpp new file mode 100644 index 000000000..f953dbf0e --- /dev/null +++ b/src/ast/ast_pp_util.cpp @@ -0,0 +1,69 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + ast_pp_util.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2015-8-6. + +Revision History: + +--*/ + +#include "ast_pp_util.h" +#include "ast_smt2_pp.h" +#include "ast_smt_pp.h" + +void ast_pp_util::collect(expr* e) { + coll.visit(e); +} + +void ast_pp_util::collect(unsigned n, expr* const* es) { + for (unsigned i = 0; i < n; ++i) { + coll.visit(es[i]); + } +} + +void ast_pp_util::collect(expr_ref_vector const& es) { + collect(es.size(), es.c_ptr()); +} + +void ast_pp_util::display_decls(std::ostream& out) { + smt2_pp_environment_dbg env(m); + ast_smt_pp pp(m); + unsigned n = coll.get_num_sorts(); + for (unsigned i = 0; i < n; ++i) { + pp.display_ast_smt2(out, coll.get_sorts()[i], 0, 0, 0); + } + n = coll.get_num_decls(); + for (unsigned i = 0; i < n; ++i) { + ast_smt2_pp(out, coll.get_func_decls()[i], env); + out << "\n"; + } +} + +void ast_pp_util::display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat) { + if (neat) { + smt2_pp_environment_dbg env(m); + for (unsigned i = 0; i < fmls.size(); ++i) { + out << "(assert "; + ast_smt2_pp(out, fmls[i], env); + out << ")\n"; + } + } + else { + ast_smt_pp ll_smt2_pp(m); + for (unsigned i = 0; i < fmls.size(); ++i) { + out << "(assert "; + ll_smt2_pp.display_expr_smt2(out, fmls[i]); + out << ")\n"; + } + } +} diff --git a/src/ast/ast_pp_util.h b/src/ast/ast_pp_util.h new file mode 100644 index 000000000..a5aac2136 --- /dev/null +++ b/src/ast/ast_pp_util.h @@ -0,0 +1,43 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + ast_pp_util.h + +Abstract: + + Utilities for printing SMT2 declarations and assertions. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-8-6. + +Revision History: + +--*/ +#ifndef AST_PP_UTIL_H_ +#define AST_PP_UTIL_H_ + +#include "decl_collector.h" + +class ast_pp_util { + ast_manager& m; + public: + + decl_collector coll; + + ast_pp_util(ast_manager& m): m(m), coll(m, false) {} + + void collect(expr* e); + + void collect(unsigned n, expr* const* es); + + void collect(expr_ref_vector const& es); + + void display_decls(std::ostream& out); + + void display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat = true); +}; + +#endif /* AST_PP_UTIL_H_ */ diff --git a/src/ast/ast_printer.h b/src/ast/ast_printer.h index 3566c62bb..78fe822f6 100644 --- a/src/ast/ast_printer.h +++ b/src/ast/ast_printer.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _AST_PRINTER_H_ -#define _AST_PRINTER_H_ +#ifndef AST_PRINTER_H_ +#define AST_PRINTER_H_ #include"ast.h" #include"ast_smt2_pp.h" diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 035e228fb..c74b4f185 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -77,6 +77,8 @@ bool smt2_pp_environment::is_indexed_fdecl(func_decl * f) const { for (i = 0; i < num; i++) { if (f->get_parameter(i).is_int()) continue; + if (f->get_parameter(i).is_rational()) + continue; if (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())) continue; break; @@ -105,9 +107,13 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { ptr_buffer fs; fs.push_back(fname); for (unsigned i = 0; i < num; i++) { - SASSERT(f->get_parameter(i).is_int() || (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))); + SASSERT(f->get_parameter(i).is_int() || + f->get_parameter(i).is_rational() || + (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))); if (f->get_parameter(i).is_int()) fs.push_back(mk_int(get_manager(), f->get_parameter(i).get_int())); + else if (f->get_parameter(i).is_rational()) + fs.push_back(mk_string(get_manager(), f->get_parameter(i).get_rational().to_string().c_str())); else fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); } @@ -335,22 +341,22 @@ format * smt2_pp_environment::pp_arith_literal(app * t, bool decimal, unsigned d } else { SASSERT(u.is_irrational_algebraic_numeral(t)); - anum const & val = u.to_irrational_algebraic_numeral(t); + anum const & val2 = u.to_irrational_algebraic_numeral(t); algebraic_numbers::manager & am = u.am(); format * vf; std::ostringstream buffer; bool is_neg = false; if (decimal) { scoped_anum abs_val(am); - am.set(abs_val, val); - if (am.is_neg(val)) { + am.set(abs_val, val2); + if (am.is_neg(val2)) { is_neg = true; am.neg(abs_val); } am.display_decimal(buffer, abs_val, decimal_prec); } else { - am.display_root_smt2(buffer, val); + am.display_root_smt2(buffer, val2); } vf = mk_string(get_manager(), buffer.str().c_str()); return is_neg ? mk_neg(vf) : vf; @@ -1159,6 +1165,26 @@ std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { return out; } +std::ostream& operator<<(std::ostream& out, expr_ref const& e) { + return out << mk_ismt2_pp(e.get(), e.get_manager()); +} + +std::ostream& operator<<(std::ostream& out, app_ref const& e) { + return out << mk_ismt2_pp(e.get(), e.get_manager()); +} + +std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) { + for (unsigned i = 0; i < e.size(); ++i) + out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + return out; +} + +std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) { + for (unsigned i = 0; i < e.size(); ++i) + out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + return out; +} + #ifdef Z3DEBUG void pp(expr const * n, ast_manager & m) { std::cout << mk_ismt2_pp(const_cast(n), m) << std::endl; diff --git a/src/ast/ast_smt2_pp.h b/src/ast/ast_smt2_pp.h index 8aac71b8c..04c3b35d4 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.h @@ -19,8 +19,8 @@ Revision History: --*/ -#ifndef _AST_SMT2_PP_H_ -#define _AST_SMT2_PP_H_ +#ifndef AST_SMT2_PP_H_ +#define AST_SMT2_PP_H_ #include"format.h" #include"params.h" @@ -110,4 +110,10 @@ struct mk_ismt2_pp { std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p); +std::ostream& operator<<(std::ostream& out, expr_ref const& e); +std::ostream& operator<<(std::ostream& out, app_ref const& e); + +std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e); +std::ostream& operator<<(std::ostream& out, app_ref_vector const& e); + #endif diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 805f3070f..9279f50fe 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -913,9 +913,9 @@ public: m_out << "("; } m_out << m_renaming.get_symbol(f->get_name()); - // if (accs.size() > 0) { + if (!accs.empty() || !m_is_smt2) { m_out << " "; - // } + } for (unsigned j = 0; j < accs.size(); ++j) { func_decl* a = accs[j]; m_out << "(" << m_renaming.get_symbol(a->get_name()) << " "; @@ -1058,7 +1058,8 @@ void ast_smt_pp::display_ast_smt2(std::ostream& strm, ast* a, unsigned indent, u void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { ptr_vector ql; - decl_collector decls(m_manager); + ast_manager& m = m_manager; + decl_collector decls(m); smt_renaming rn; for (unsigned i = 0; i < m_assumptions.size(); ++i) { @@ -1069,7 +1070,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { } decls.visit(n); - if (m_manager.is_proof(n)) { + if (m.is_proof(n)) { strm << "("; } if (m_benchmark_name != symbol::null) { @@ -1078,7 +1079,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { if (m_source_info != symbol::null && m_source_info != symbol("")) { strm << "; :source { " << m_source_info << " }\n"; } - if (m_manager.is_bool(n)) { + if (m.is_bool(n)) { strm << "(set-info :status " << m_status << ")\n"; } if (m_category != symbol::null && m_category != symbol("")) { @@ -1095,7 +1096,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { for (unsigned i = 0; i < decls.get_num_sorts(); ++i) { sort* s = decls.get_sorts()[i]; if (!(*m_is_declared)(s)) { - smt_printer p(strm, m_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0); + smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); p.pp_sort_decl(sort_mark, s); } } @@ -1103,7 +1104,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { for (unsigned i = 0; i < decls.get_num_decls(); ++i) { func_decl* d = decls.get_func_decls()[i]; if (!(*m_is_declared)(d)) { - smt_printer p(strm, m_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0); + smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); p(d); strm << "\n"; } @@ -1112,34 +1113,36 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { for (unsigned i = 0; i < decls.get_num_preds(); ++i) { func_decl* d = decls.get_pred_decls()[i]; if (!(*m_is_declared)(d)) { - smt_printer p(strm, m_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0); + smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); p(d); strm << "\n"; } } for (unsigned i = 0; i < m_assumptions.size(); ++i) { - strm << "(assert\n"; - smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0); - p(m_assumptions[i].get()); - strm << ")\n"; + smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); + strm << "(assert\n "; + p(m_assumptions[i].get()); + strm << ")\n"; } for (unsigned i = 0; i < m_assumptions_star.size(); ++i) { - strm << "(assert\n"; - smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0); - p(m_assumptions_star[i].get()); + smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); + strm << "(assert\n "; + p(m_assumptions_star[i].get()); strm << ")\n"; } - smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0); - if (m_manager.is_bool(n)) { - strm << "(assert\n"; - p(n); - strm << ")\n"; + smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 0); + if (m.is_bool(n)) { + if (!m.is_true(n)) { + strm << "(assert\n "; + p(n); + strm << ")\n"; + } strm << "(check-sat)\n"; } - else if (m_manager.is_proof(n)) { + else if (m.is_proof(n)) { strm << "(proof\n"; p(n); strm << "))\n"; diff --git a/src/ast/ast_smt_pp.h b/src/ast/ast_smt_pp.h index 97527759a..59b1596fa 100644 --- a/src/ast/ast_smt_pp.h +++ b/src/ast/ast_smt_pp.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _AST_SMT_PP_H_ -#define _AST_SMT_PP_H_ +#ifndef AST_SMT_PP_H_ +#define AST_SMT_PP_H_ #include"ast.h" #include diff --git a/src/ast/ast_trail.h b/src/ast/ast_trail.h new file mode 100644 index 000000000..4e2c542fd --- /dev/null +++ b/src/ast/ast_trail.h @@ -0,0 +1,76 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + ast_trail.h + +Abstract: + + + +Author: + + Leonardo de Moura (leonardo) 2008-06-02. + +Revision History: + + Extracted AST specific features from trail.h + nbjorner 2014-9-28 + +--*/ +#ifndef AST_TRAIL_H_ +#define AST_TRAIL_H_ + +#include"ast.h" +#include"trail.h" + + +template +class ast2ast_trailmap { + ref_vector m_domain; + ref_vector m_range; + obj_map m_map; +public: + ast2ast_trailmap(ast_manager& m): + m_domain(m), + m_range(m), + m_map() + {} + + bool find(S* s, T*& t) { + return m_map.find(s,t); + } + + void insert(S* s, T* t) { + SASSERT(!m_map.contains(s)); + m_domain.push_back(s); + m_range.push_back(t); + m_map.insert(s,t); + } + + void pop() { + SASSERT(!m_domain.empty()); + m_map.remove(m_domain.back()); + m_domain.pop_back(); + m_range.pop_back(); + } +}; + +template +class ast2ast_trail : public trail { + ast2ast_trailmap& m_map; +public: + ast2ast_trail(ast2ast_trailmap& m, S* s, T* t) : + m_map(m) { + m.insert(s,t); + } + + virtual void undo(Ctx& ctx) { + m_map.pop(); + } +}; + + +#endif /* AST_TRAIL_H_ */ + diff --git a/src/ast/ast_translation.h b/src/ast/ast_translation.h index 85c6af7ac..b1b12c1c6 100644 --- a/src/ast/ast_translation.h +++ b/src/ast/ast_translation.h @@ -18,8 +18,8 @@ Revision History: 2011-05-26: New local translation class. --*/ -#ifndef _AST_TRANSLATION_H_ -#define _AST_TRANSLATION_H_ +#ifndef AST_TRANSLATION_H_ +#define AST_TRANSLATION_H_ #include"ast.h" diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index d77deca01..94e7b642c 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -44,12 +44,12 @@ app * mk_list_assoc_app(ast_manager & m, family_id fid, decl_kind k, unsigned nu return mk_list_assoc_app(m, decl, num_args, args); } -bool is_well_formed_vars(ptr_vector& bound, expr* e) { +bool is_well_formed_vars(ptr_vector& bound, expr * top) { ptr_vector todo; ast_mark mark; - todo.push_back(e); + todo.push_back(top); while (!todo.empty()) { - expr* e = todo.back(); + expr * e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) { continue; @@ -191,3 +191,116 @@ expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) } return mk_and(m, new_diseqs.size(), new_diseqs.c_ptr()); } + +void flatten_and(expr_ref_vector& result) { + ast_manager& m = result.get_manager(); + expr* e1, *e2, *e3; + for (unsigned i = 0; i < result.size(); ++i) { + if (m.is_and(result[i].get())) { + app* a = to_app(result[i].get()); + unsigned num_args = a->get_num_args(); + for (unsigned j = 0; j < num_args; ++j) { + result.push_back(a->get_arg(j)); + } + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { + result[i] = e2; + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_or(e1)) { + app* a = to_app(e1); + unsigned num_args = a->get_num_args(); + for (unsigned j = 0; j < num_args; ++j) { + result.push_back(m.mk_not(a->get_arg(j))); + } + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) { + result.push_back(e2); + result[i] = m.mk_not(e3); + --i; + } + else if (m.is_true(result[i].get()) || + (m.is_not(result[i].get(), e1) && + m.is_false(e1))) { + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_false(result[i].get()) || + (m.is_not(result[i].get(), e1) && + m.is_true(e1))) { + result.reset(); + result.push_back(m.mk_false()); + return; + } + } +} + +void flatten_and(expr* fml, expr_ref_vector& result) { + SASSERT(result.get_manager().is_bool(fml)); + result.push_back(fml); + flatten_and(result); +} + +void flatten_or(expr_ref_vector& result) { + ast_manager& m = result.get_manager(); + expr* e1, *e2, *e3; + for (unsigned i = 0; i < result.size(); ++i) { + if (m.is_or(result[i].get())) { + app* a = to_app(result[i].get()); + unsigned num_args = a->get_num_args(); + for (unsigned j = 0; j < num_args; ++j) { + result.push_back(a->get_arg(j)); + } + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { + result[i] = e2; + --i; + } + else if (m.is_not(result[i].get(), e1) && m.is_and(e1)) { + app* a = to_app(e1); + unsigned num_args = a->get_num_args(); + for (unsigned j = 0; j < num_args; ++j) { + result.push_back(m.mk_not(a->get_arg(j))); + } + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_implies(result[i].get(),e2,e3)) { + result.push_back(e3); + result[i] = m.mk_not(e2); + --i; + } + else if (m.is_false(result[i].get()) || + (m.is_not(result[i].get(), e1) && + m.is_true(e1))) { + result[i] = result.back(); + result.pop_back(); + --i; + } + else if (m.is_true(result[i].get()) || + (m.is_not(result[i].get(), e1) && + m.is_false(e1))) { + result.reset(); + result.push_back(m.mk_true()); + return; + } + } +} + + +void flatten_or(expr* fml, expr_ref_vector& result) { + SASSERT(result.get_manager().is_bool(fml)); + result.push_back(fml); + flatten_or(result); +} diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index 1e813b1b0..87a3a90d9 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _AST_UTIL_H_ -#define _AST_UTIL_H_ +#ifndef AST_UTIL_H_ +#define AST_UTIL_H_ #include"ast.h" #include"obj_hashtable.h" @@ -126,5 +126,18 @@ expr * mk_not(ast_manager & m, expr * arg); */ expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args); -#endif /* _AST_UTIL_H_ */ +/** + \brief Collect top-level conjunctions and disjunctions. +*/ + +void flatten_and(expr_ref_vector& result); + +void flatten_and(expr* fml, expr_ref_vector& result); + +void flatten_or(expr_ref_vector& result); + +void flatten_or(expr* fml, expr_ref_vector& result); + + +#endif /* AST_UTIL_H_ */ diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index b056ded36..f65462d1b 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -501,13 +501,17 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p func_decl * r = mk_func_decl(k, bv_size); if (r != 0) { if (arity != r->get_arity()) { - m_manager->raise_exception("declared arity mismatches supplied arity"); - return 0; + if (r->get_info()->is_associative()) + arity = r->get_arity(); + else { + m_manager->raise_exception("declared arity mismatches supplied arity"); + return 0; + } } for (unsigned i = 0; i < arity; ++i) { if (domain[i] != r->get_domain(i)) { m_manager->raise_exception("declared sorts do not match supplied sorts"); - return 0; + return 0; } } return r; @@ -566,6 +570,7 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { + ast_manager& m = *m_manager; int bv_size; if (k == OP_INT2BV && get_int2bv_size(num_parameters, parameters, bv_size)) { // bv_size is filled in. @@ -589,11 +594,35 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); } else if (num_args == 0 || !get_bv_size(args[0], bv_size)) { - m_manager->raise_exception("operator is applied to arguments of the wrong sort"); + m.raise_exception("operator is applied to arguments of the wrong sort"); return 0; } func_decl * r = mk_func_decl(k, bv_size); if (r != 0) { + if (num_args != r->get_arity()) { + if (r->get_info()->is_associative()) { + sort * fs = r->get_domain(0); + for (unsigned i = 0; i < num_args; ++i) { + if (m.get_sort(args[i]) != fs) { + m_manager->raise_exception("declared sorts do not match supplied sorts"); + return 0; + } + } + return r; + } + else { + m.raise_exception("declared arity mismatches supplied arity"); + return 0; + } + } + for (unsigned i = 0; i < num_args; ++i) { + if (m.get_sort(args[i]) != r->get_domain(i)) { + std::ostringstream buffer; + buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " does not match declaration " << mk_pp(r, m); + m.raise_exception(buffer.str().c_str()); + return 0; + } + } return r; } return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index c5ebfb2d9..9343e54a2 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _BV_DECL_PLUGIN_H_ -#define _BV_DECL_PLUGIN_H_ +#ifndef BV_DECL_PLUGIN_H_ +#define BV_DECL_PLUGIN_H_ #include"ast.h" @@ -389,5 +389,5 @@ public: app * mk_bv(unsigned n, expr* const* es) { return m_manager.mk_app(get_fid(), OP_MKBV, n, es); } }; -#endif /* _BV_DECL_PLUGIN_H_ */ +#endif /* BV_DECL_PLUGIN_H_ */ diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index 00f026f55..c185540b7 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -149,10 +149,10 @@ enum status { */ static bool is_recursive_datatype(parameter const * parameters) { unsigned num_types = parameters[0].get_int(); - unsigned tid = parameters[1].get_int(); + unsigned top_tid = parameters[1].get_int(); buffer already_found(num_types, WHITE); buffer todo; - todo.push_back(tid); + todo.push_back(top_tid); while (!todo.empty()) { unsigned tid = todo.back(); if (already_found[tid] == BLACK) { @@ -198,11 +198,11 @@ static bool is_recursive_datatype(parameter const * parameters) { */ static sort_size get_datatype_size(parameter const * parameters) { unsigned num_types = parameters[0].get_int(); - unsigned tid = parameters[1].get_int(); + unsigned top_tid = parameters[1].get_int(); buffer szs(num_types, sort_size()); buffer already_found(num_types, WHITE); buffer todo; - todo.push_back(tid); + todo.push_back(top_tid); while (!todo.empty()) { unsigned tid = todo.back(); if (already_found[tid] == BLACK) { @@ -280,7 +280,7 @@ static sort_size get_datatype_size(parameter const * parameters) { } } } - return szs[tid]; + return szs[top_tid]; } /** @@ -422,8 +422,55 @@ static sort * get_type(ast_manager & m, family_id datatype_fid, sort * source_da } } +func_decl * datatype_decl_plugin::mk_update_field( + unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) { + decl_kind k = OP_DT_UPDATE_FIELD; + ast_manager& m = *m_manager; + + if (num_parameters != 1 || !parameters[0].is_ast()) { + m.raise_exception("invalid parameters for datatype field update"); + return 0; + } + if (arity != 2) { + m.raise_exception("invalid number of arguments for datatype field update"); + return 0; + } + func_decl* acc = 0; + if (is_func_decl(parameters[0].get_ast())) { + acc = to_func_decl(parameters[0].get_ast()); + } + if (acc && !get_util().is_accessor(acc)) { + acc = 0; + } + if (!acc) { + m.raise_exception("datatype field update requires a datatype accessor as the second argument"); + return 0; + } + sort* dom = acc->get_domain(0); + sort* rng = acc->get_range(); + if (dom != domain[0]) { + m.raise_exception("first argument to field update should be a data-type"); + return 0; + } + if (rng != domain[1]) { + std::ostringstream buffer; + buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m) + << " instead of " << mk_ismt2_pp(domain[1], m); + m.raise_exception(buffer.str().c_str()); + return 0; + } + range = domain[0]; + func_decl_info info(m_family_id, k, num_parameters, parameters); + return m.mk_func_decl(symbol("update_field"), arity, domain, range, info); +} + func_decl * datatype_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { + + if (k == OP_DT_UPDATE_FIELD) { + return mk_update_field(num_parameters, parameters, arity, domain, range); + } if (num_parameters < 2 || !parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { m_manager->raise_exception("invalid parameters for datatype operator"); return 0; @@ -521,6 +568,9 @@ func_decl * datatype_decl_plugin::mk_func_decl(decl_kind k, unsigned num_paramet return m_manager->mk_func_decl(a_name, arity, domain, a_type, info); } break; + case OP_DT_UPDATE_FIELD: + UNREACHABLE(); + return 0; default: m_manager->raise_exception("invalid datatype operator kind"); return 0; @@ -607,8 +657,8 @@ bool datatype_decl_plugin::is_fully_interp(sort const * s) const { for (unsigned tid = 0; tid < num_types; tid++) { unsigned o = parameters[2 + 2*tid + 1].get_int(); // constructor offset unsigned num_constructors = parameters[o].get_int(); - for (unsigned s = 1; s <= num_constructors; s++) { - unsigned k_i = parameters[o + s].get_int(); + for (unsigned si = 1; si <= num_constructors; si++) { + unsigned k_i = parameters[o + si].get_int(); unsigned num_accessors = parameters[k_i + 2].get_int(); unsigned r = 0; for (; r < num_accessors; r++) { @@ -672,6 +722,13 @@ bool datatype_decl_plugin::is_value(app * e) const { return true; } +void datatype_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { + if (logic == symbol::null) { + op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD)); + } +} + + datatype_util::datatype_util(ast_manager & m): m_manager(m), m_family_id(m.mk_family_id("datatype")), diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 3f7f01364..99be75493 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DATATYPE_DECL_PLUGIN_H_ -#define _DATATYPE_DECL_PLUGIN_H_ +#ifndef DATATYPE_DECL_PLUGIN_H_ +#define DATATYPE_DECL_PLUGIN_H_ #include"ast.h" #include"tptr.h" @@ -32,6 +32,7 @@ enum datatype_op_kind { OP_DT_CONSTRUCTOR, OP_DT_RECOGNISER, OP_DT_ACCESSOR, + OP_DT_UPDATE_FIELD, LAST_DT_OP }; @@ -149,8 +150,14 @@ public: virtual bool is_unique_value(app * e) const { return is_value(e); } + virtual void get_op_names(svector & op_names, symbol const & logic); + private: bool is_value_visit(expr * arg, ptr_buffer & todo) const; + + func_decl * mk_update_field( + unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); }; class datatype_util { @@ -181,9 +188,11 @@ public: bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); } bool is_recognizer(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); } bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); } + bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); } bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); } bool is_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); } bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); } + bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); } ptr_vector const * get_datatype_constructors(sort * ty); unsigned get_datatype_num_constructors(sort * ty) { SASSERT(is_datatype(ty)); @@ -205,5 +214,5 @@ public: }; -#endif /* _DATATYPE_DECL_PLUGIN_H_ */ +#endif /* DATATYPE_DECL_PLUGIN_H_ */ diff --git a/src/ast/decl_collector.h b/src/ast/decl_collector.h index 4bcec94aa..678f3805d 100644 --- a/src/ast/decl_collector.h +++ b/src/ast/decl_collector.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _SMT_DECL_COLLECTOR_H_ -#define _SMT_DECL_COLLECTOR_H_ +#ifndef SMT_DECL_COLLECTOR_H_ +#define SMT_DECL_COLLECTOR_H_ #include"ast.h" @@ -43,6 +43,8 @@ public: ast_manager & m() { return m_manager; } void visit(ast * n); + void visit(unsigned n, expr* const* es); + void visit(expr_ref_vector const& es); unsigned get_num_sorts() const { return m_sorts.size(); } unsigned get_num_decls() const { return m_decls.size(); } diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index badf8a59d..305ac1779 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -44,7 +44,8 @@ namespace datalog { m_num_sym("N"), m_lt_sym("<"), m_le_sym("<="), - m_rule_sym("R") + m_rule_sym("R"), + m_min_sym("min") { } @@ -490,6 +491,66 @@ namespace datalog { return m_manager->mk_func_decl(m_clone_sym, 1, &s, s, info); } + /** + In SMT2 syntax, we can write \c ((_ min R N) v_0 v_1 ... v_k)) where 0 <= N <= k, + R is a relation of sort V_0 x V_1 x ... x V_k and each v_i is a zero-arity function + (also known as a "constant" in SMT2 parlance) whose range is of sort V_i. + + Example: + + (define-sort number_t () (_ BitVec 2)) + (declare-rel numbers (number_t number_t)) + (declare-rel is_min (number_t number_t)) + + (declare-var x number_t) + (declare-var y number_t) + + (rule (numbers #b00 #b11)) + (rule (numbers #b00 #b01)) + + (rule (=> (and (numbers x y) ((_ min numbers 1) x y)) (is_min x y))) + + This says that we want to find the mininum y grouped by x. + */ + func_decl * dl_decl_plugin::mk_min(decl_kind k, unsigned num_parameters, parameter const * parameters) { + if (num_parameters < 2) { + m_manager->raise_exception("invalid min aggregate definition due to missing parameters"); + return 0; + } + + parameter const & relation_parameter = parameters[0]; + if (!relation_parameter.is_ast() || !is_func_decl(relation_parameter.get_ast())) { + m_manager->raise_exception("invalid min aggregate definition, first parameter is not a function declaration"); + return 0; + } + + func_decl* f = to_func_decl(relation_parameter.get_ast()); + if (!m_manager->is_bool(f->get_range())) { + m_manager->raise_exception("invalid min aggregate definition, first paramater must be a predicate"); + return 0; + } + + parameter const & min_col_parameter = parameters[1]; + if (!min_col_parameter.is_int()) { + m_manager->raise_exception("invalid min aggregate definition, second parameter must be an integer"); + return 0; + } + + if (min_col_parameter.get_int() < 0) { + m_manager->raise_exception("invalid min aggregate definition, second parameter must be non-negative"); + return 0; + } + + if ((unsigned)min_col_parameter.get_int() >= f->get_arity()) { + m_manager->raise_exception("invalid min aggregate definition, second parameter exceeds the arity of the relation"); + return 0; + } + + func_decl_info info(m_family_id, k, num_parameters, parameters); + SASSERT(f->get_info() == 0); + return m_manager->mk_func_decl(m_min_sym, f->get_arity(), f->get_domain(), f->get_range(), info); + } + func_decl * dl_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { @@ -617,6 +678,9 @@ namespace datalog { break; } + case OP_DL_MIN: + return mk_min(k, num_parameters, parameters); + default: m_manager->raise_exception("operator not recognized"); return 0; @@ -627,7 +691,7 @@ namespace datalog { } void dl_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { - + op_names.push_back(builtin_name(m_min_sym.bare_str(), OP_DL_MIN)); } void dl_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index 65b00235c..4491fe180 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_DECL_PLUGIN_H_ -#define _DL_DECL_PLUGIN_H_ +#ifndef DL_DECL_PLUGIN_H_ +#define DL_DECL_PLUGIN_H_ #include"ast.h" #include "arith_decl_plugin.h" @@ -50,6 +50,7 @@ namespace datalog { OP_DL_LT, OP_DL_REP, OP_DL_ABS, + OP_DL_MIN, LAST_RA_OP }; @@ -71,6 +72,7 @@ namespace datalog { symbol m_lt_sym; symbol m_le_sym; symbol m_rule_sym; + symbol m_min_sym; bool check_bounds(char const* msg, unsigned low, unsigned up, unsigned val) const; bool check_domain(unsigned low, unsigned up, unsigned val) const; @@ -94,12 +96,69 @@ namespace datalog { func_decl * mk_compare(decl_kind k, symbol const& sym, sort*const* domain); func_decl * mk_clone(sort* r); func_decl * mk_rule(unsigned arity); + func_decl * mk_min(decl_kind k, unsigned num_parameters, parameter const * parameters); sort * mk_finite_sort(unsigned num_params, parameter const* params); sort * mk_relation_sort(unsigned num_params, parameter const* params); sort * mk_rule_sort(); public: + /** + Is \c decl a min aggregation function? + */ + static bool is_aggregate(const func_decl* const decl) + { + return decl->get_decl_kind() == OP_DL_MIN; + } + + /** + \pre: is_aggregate(aggregate) + + \returns function declaration of predicate which is subject to min aggregation function + */ + static func_decl * min_func_decl(const func_decl* const aggregate) + { + SASSERT(is_aggregate(aggregate)); + parameter const & relation_parameter = aggregate->get_parameter(0); + return to_func_decl(relation_parameter.get_ast()); + } + + /** + \pre: is_aggregate(aggregate) + + \returns column identifier (starting at zero) which is minimized by aggregation function + */ + static unsigned min_col(const func_decl* const aggregate) + { + SASSERT(is_aggregate(aggregate)); + return (unsigned)aggregate->get_parameter(1).get_int(); + } + + /** + \pre: is_aggregate(aggregate) + + \returns column identifiers for the "group by" in the given min aggregation function + */ + static unsigned_vector group_by_cols(const func_decl* const aggregate) + { + SASSERT(is_aggregate(aggregate)); + unsigned _min_col = min_col(aggregate); + if (aggregate->get_arity() == 0U) + return unsigned_vector(); + + unsigned col_num = 0; + unsigned_vector cols(aggregate->get_arity() - 1U); + for (unsigned i = 0; i < cols.size(); ++i, ++col_num) + { + if (col_num == _min_col) + ++col_num; + + cols[i] = col_num; + } + + return cols; + } + dl_decl_plugin(); virtual ~dl_decl_plugin() {} @@ -214,5 +273,5 @@ namespace datalog { }; }; -#endif /* _DL_DECL_PLUGIN_H_ */ +#endif /* DL_DECL_PLUGIN_H_ */ diff --git a/src/ast/expr2polynomial.cpp b/src/ast/expr2polynomial.cpp index fbabcb150..097d77cbc 100644 --- a/src/ast/expr2polynomial.cpp +++ b/src/ast/expr2polynomial.cpp @@ -367,16 +367,16 @@ struct expr2polynomial::imp { begin_loop: checkpoint(); frame & fr = m_frame_stack.back(); - app * t = fr.m_curr; - TRACE("expr2polynomial", tout << "processing: " << fr.m_idx << "\n" << mk_ismt2_pp(t, m()) << "\n";); - unsigned num_args = t->get_num_args(); + app * a = fr.m_curr; + TRACE("expr2polynomial", tout << "processing: " << fr.m_idx << "\n" << mk_ismt2_pp(a, m()) << "\n";); + unsigned num_args = a->get_num_args(); while (fr.m_idx < num_args) { - expr * arg = t->get_arg(fr.m_idx); + expr * arg = a->get_arg(fr.m_idx); fr.m_idx++; if (!visit(arg)) goto begin_loop; } - process_app(t); + process_app(a); m_frame_stack.pop_back(); } } diff --git a/src/ast/expr2polynomial.h b/src/ast/expr2polynomial.h index 8934f3f24..6b2fd6116 100644 --- a/src/ast/expr2polynomial.h +++ b/src/ast/expr2polynomial.h @@ -17,8 +17,8 @@ Author: Notes: --*/ -#ifndef _EXPR2POLYNOMIAL_H_ -#define _EXPR2POLYNOMIAL_H_ +#ifndef EXPR2POLYNOMIAL_H_ +#define EXPR2POLYNOMIAL_H_ #include"ast.h" #include"polynomial.h" diff --git a/src/ast/expr2var.cpp b/src/ast/expr2var.cpp index 1f5a8fe5c..a0d93a620 100644 --- a/src/ast/expr2var.cpp +++ b/src/ast/expr2var.cpp @@ -73,5 +73,22 @@ void expr2var::reset() { dec_ref_map_keys(m(), m_mapping); SASSERT(m_mapping.empty()); m_recent_exprs.reset(); + m_recent_lim.reset(); m_interpreted_vars = false; } + +void expr2var::push() { + m_recent_lim.push_back(m_recent_exprs.size()); +} + +void expr2var::pop(unsigned num_scopes) { + if (num_scopes > 0) { + unsigned sz = m_recent_lim[m_recent_lim.size() - num_scopes]; + for (unsigned i = sz; i < m_recent_exprs.size(); ++i) { + m_mapping.erase(m_recent_exprs[i]); + m().dec_ref(m_recent_exprs[i]); + } + m_recent_exprs.shrink(sz); + m_recent_lim.shrink(m_recent_lim.size() - num_scopes); + } +} diff --git a/src/ast/expr2var.h b/src/ast/expr2var.h index 35b5da632..43d9c31f4 100644 --- a/src/ast/expr2var.h +++ b/src/ast/expr2var.h @@ -20,8 +20,8 @@ Author: Notes: --*/ -#ifndef _EXPR2VAR_H_ -#define _EXPR2VAR_H_ +#ifndef EXPR2VAR_H_ +#define EXPR2VAR_H_ #include"ast.h" #include"obj_hashtable.h" @@ -39,6 +39,7 @@ protected: ast_manager & m_manager; expr2var_mapping m_mapping; ptr_vector m_recent_exprs; + unsigned_vector m_recent_lim; bool m_interpreted_vars; public: expr2var(ast_manager & m); @@ -62,7 +63,7 @@ public: iterator begin() const { return m_mapping.begin(); } iterator end() const { return m_mapping.end(); } - void reset_recent() { m_recent_exprs.reset(); } + void reset_recent() { SASSERT(m_recent_lim.empty()); m_recent_exprs.reset(); } // Iterators for traversing the recently registered expressions. // The set of recent registered expressions is reset by using reset_recent(). @@ -70,6 +71,9 @@ public: recent_iterator end_recent() const { return m_recent_exprs.end(); } void reset(); + + void push(); + void pop(unsigned num_scopes); }; #endif diff --git a/src/ast/expr_abstract.h b/src/ast/expr_abstract.h index 3d9f3960f..7f43ecf83 100644 --- a/src/ast/expr_abstract.h +++ b/src/ast/expr_abstract.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _EXPR_ABSTRACT_H_ -#define _EXPR_ABSTRACT_H_ +#ifndef EXPR_ABSTRACT_H_ +#define EXPR_ABSTRACT_H_ #include"ast.h" diff --git a/src/ast/expr_delta_pair.h b/src/ast/expr_delta_pair.h index f1994db6a..f4cc40ae4 100644 --- a/src/ast/expr_delta_pair.h +++ b/src/ast/expr_delta_pair.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _EXPR_DELTA_PAIR_H_ -#define _EXPR_DELTA_PAIR_H_ +#ifndef EXPR_DELTA_PAIR_H_ +#define EXPR_DELTA_PAIR_H_ /** \brief Auxiliary structure used to cache the intermediate results of the variable substitution procedure. @@ -32,5 +32,5 @@ struct expr_delta_pair { bool operator==(const expr_delta_pair & e) const { return m_node == e.m_node && m_delta == e.m_delta; } }; -#endif /* _EXPR_DELTA_PAIR_H_ */ +#endif /* EXPR_DELTA_PAIR_H_ */ diff --git a/src/ast/expr_functors.h b/src/ast/expr_functors.h index f2016c8ed..32560e139 100644 --- a/src/ast/expr_functors.h +++ b/src/ast/expr_functors.h @@ -19,8 +19,8 @@ Revision History: --*/ -#ifndef __EXPR_FUNCTORS_H__ -#define __EXPR_FUNCTORS_H__ +#ifndef EXPR_FUNCTORS_H_ +#define EXPR_FUNCTORS_H_ #include "ast.h" #include "expr_map.h" diff --git a/src/ast/expr_map.h b/src/ast/expr_map.h index 5ab6545f8..ddd94ea13 100644 --- a/src/ast/expr_map.h +++ b/src/ast/expr_map.h @@ -18,8 +18,8 @@ Author: Notes: --*/ -#ifndef _EXPR_MAP_H_ -#define _EXPR_MAP_H_ +#ifndef EXPR_MAP_H_ +#define EXPR_MAP_H_ #include"ast.h" #include"obj_hashtable.h" @@ -44,6 +44,10 @@ public: void erase(expr * k); void reset(); void flush(); + void set_store_proofs(bool f) { + if (m_store_proofs != f) flush(); + m_store_proofs = f; + } }; #endif diff --git a/src/ast/expr_stat.h b/src/ast/expr_stat.h index 7e94e5212..259751d18 100644 --- a/src/ast/expr_stat.h +++ b/src/ast/expr_stat.h @@ -19,8 +19,8 @@ Author: Revision History: --*/ -#ifndef _EXPR_STAT_H_ -#define _EXPR_STAT_H_ +#ifndef EXPR_STAT_H_ +#define EXPR_STAT_H_ class expr; @@ -47,4 +47,4 @@ void get_expr_stat(expr * n, expr_stat & r); */ unsigned get_symbol_count(expr * n); -#endif /* _EXPR_STAT_H_ */ +#endif /* EXPR_STAT_H_ */ diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h index 507228f98..073b56110 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _EXPR_SUBSTITUTION_H_ -#define _EXPR_SUBSTITUTION_H_ +#ifndef EXPR_SUBSTITUTION_H_ +#define EXPR_SUBSTITUTION_H_ #include"ast.h" diff --git a/src/ast/for_each_ast.h b/src/ast/for_each_ast.h index 0fca62ca9..a254ed058 100644 --- a/src/ast/for_each_ast.h +++ b/src/ast/for_each_ast.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _FOR_EACH_AST_H_ -#define _FOR_EACH_AST_H_ +#ifndef FOR_EACH_AST_H_ +#define FOR_EACH_AST_H_ #include"ast.h" #include"trace.h" @@ -270,5 +270,5 @@ private: } }; -#endif /* _FOR_EACH_AST_H_ */ +#endif /* FOR_EACH_AST_H_ */ diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index d0e110ec8..3f5dbaac7 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _FOR_EACH_EXPR_H_ -#define _FOR_EACH_EXPR_H_ +#ifndef FOR_EACH_EXPR_H_ +#define FOR_EACH_EXPR_H_ #include"ast.h" #include"trace.h" @@ -153,5 +153,5 @@ unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited); bool has_skolem_functions(expr * n); -#endif /* _FOR_EACH_EXPR_H_ */ +#endif /* FOR_EACH_EXPR_H_ */ diff --git a/src/ast/format.h b/src/ast/format.h index 780e76251..a41ed9bd9 100644 --- a/src/ast/format.h +++ b/src/ast/format.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _FORMAT_H_ -#define _FORMAT_H_ +#ifndef FORMAT_H_ +#define FORMAT_H_ #include"ast.h" @@ -193,5 +193,5 @@ namespace format_ns { }; -#endif /* _FORMAT_H_ */ +#endif /* FORMAT_H_ */ diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 932451ff5..b96874667 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -48,19 +48,31 @@ void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { SASSERT(is_app_of(a, m_plugin->get_family_id(), OP_FPA_FP)); SASSERT(is_app_of(b, m_plugin->get_family_id(), OP_FPA_FP)); - expr_ref sgn(m), s(m), e(m); - m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), sgn); - m_simp.mk_eq(to_app(a)->get_arg(1), to_app(b)->get_arg(1), e); - m_simp.mk_eq(to_app(a)->get_arg(2), to_app(b)->get_arg(2), s); + TRACE("fpa2bv", tout << "mk_eq a=" << mk_ismt2_pp(a, m) << std::endl; + tout << "mk_eq b=" << mk_ismt2_pp(b, m) << std::endl;); + + expr_ref eq_sgn(m), eq_exp(m), eq_sig(m); + m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), eq_sgn); + m_simp.mk_eq(to_app(a)->get_arg(1), to_app(b)->get_arg(1), eq_exp); + m_simp.mk_eq(to_app(a)->get_arg(2), to_app(b)->get_arg(2), eq_sig); + + dbg_decouple("fpa2bv_eq_sgn", eq_sgn); + dbg_decouple("fpa2bv_eq_exp", eq_exp); + dbg_decouple("fpa2bv_eq_sig", eq_sig); + + expr_ref both_the_same(m); + m_simp.mk_and(eq_sgn, eq_exp, eq_sig, both_the_same); + dbg_decouple("fpa2bv_eq_both_the_same", both_the_same); // The SMT FPA theory asks for _one_ NaN value, but the bit-blasting // has many, like IEEE754. This encoding of equality makes it look like // a single NaN again. - expr_ref both_the_same(m), a_is_nan(m), b_is_nan(m), both_are_nan(m); - m_simp.mk_and(sgn, s, e, both_the_same); + expr_ref a_is_nan(m), b_is_nan(m), both_are_nan(m); mk_is_nan(a, a_is_nan); mk_is_nan(b, b_is_nan); m_simp.mk_and(a_is_nan, b_is_nan, both_are_nan); + dbg_decouple("fpa2bv_eq_both_are_nan", both_are_nan); + m_simp.mk_or(both_are_nan, both_the_same, result); } @@ -81,6 +93,20 @@ void fpa2bv_converter::mk_ite(expr * c, expr * t, expr * f, expr_ref & result) { mk_fp(sgn, e, s, result); } +void fpa2bv_converter::mk_distinct(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + // Note: in SMT there is only one NaN, so multiple of them are considered + // equal, thus (distinct NaN NaN) is false, even if the two NaNs have + // different bitwise representations (see also mk_eq). + result = m.mk_true(); + for (unsigned i = 0; i < num; i++) { + for (unsigned j = i+1; j < num; j++) { + expr_ref eq(m); + mk_eq(args[i], args[j], eq); + m_simp.mk_and(result, m.mk_not(eq), result); + } + } +} + void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 0); SASSERT(f->get_num_parameters() == 1); @@ -181,87 +207,76 @@ void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) mk_fp(sgn, e, s, result); } +void fpa2bv_converter::mk_uninterpreted_output(sort * rng, func_decl * fbv, expr_ref_buffer & new_args, expr_ref & result) { + if (m_util.is_float(rng)) { + unsigned ebits = m_util.get_ebits(rng); + unsigned sbits = m_util.get_sbits(rng); + unsigned bv_sz = ebits + sbits; + + app_ref na(m); + na = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); + mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, na), + m_bv_util.mk_extract(bv_sz - 2, sbits - 1, na), + m_bv_util.mk_extract(sbits - 2, 0, na), + result); + } + else + result = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); +} + void fpa2bv_converter::mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - TRACE("fpa2bv_dbg", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); + TRACE("fpa2bv", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); SASSERT(f->get_arity() == num); expr_ref_buffer new_args(m); - for (unsigned i = 0; i < num ; i ++) - if (is_float(args[i])) - { - expr * sgn, * sig, * exp; - split_fp(args[i], sgn, exp, sig); - new_args.push_back(sgn); - new_args.push_back(sig); - new_args.push_back(exp); - } - else - new_args.push_back(args[i]); - - func_decl * fd; - func_decl_triple fd3; - if (m_uf2bvuf.find(f, fd)) { - result = m.mk_app(fd, new_args.size(), new_args.c_ptr()); - } - else if (m_uf23bvuf.find(f, fd3)) - { - expr_ref a_sgn(m), a_sig(m), a_exp(m); - a_sgn = m.mk_app(fd3.f_sgn, new_args.size(), new_args.c_ptr()); - a_sig = m.mk_app(fd3.f_sig, new_args.size(), new_args.c_ptr()); - a_exp = m.mk_app(fd3.f_exp, new_args.size(), new_args.c_ptr()); - mk_fp(a_sgn, a_exp, a_sig, result); - } - else { - sort_ref_buffer new_domain(m); - - for (unsigned i = 0; i < f->get_arity() ; i ++) - if (is_float(f->get_domain()[i])) - { - new_domain.push_back(m_bv_util.mk_sort(1)); - new_domain.push_back(m_bv_util.mk_sort(m_util.get_sbits(f->get_domain()[i])-1)); - new_domain.push_back(m_bv_util.mk_sort(m_util.get_ebits(f->get_domain()[i]))); - } - else - new_domain.push_back(f->get_domain()[i]); - - if (!is_float(f->get_range())) - { - func_decl * fbv = m.mk_func_decl(f->get_name(), new_domain.size(), new_domain.c_ptr(), f->get_range(), *f->get_info()); - TRACE("fpa2bv_dbg", tout << "New UF func_decl : " << mk_ismt2_pp(fbv, m) << std::endl; ); - m_uf2bvuf.insert(f, fbv); - m.inc_ref(f); - m.inc_ref(fbv); - result = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); + for (unsigned i = 0; i < num; i++) { + if (is_float(args[i])) { + expr * sgn, *exp, *sig; + split_fp(args[i], sgn, exp, sig); + expr * args[3] = { sgn, exp, sig }; + new_args.push_back(m_bv_util.mk_concat(3, args)); } else - { - string_buffer<> name_buffer; - name_buffer.reset(); name_buffer << f->get_name() << ".sgn"; - func_decl * f_sgn = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(1)); - name_buffer.reset(); name_buffer << f->get_name() << ".sig"; - func_decl * f_sig = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_sbits(f->get_range())-1)); - name_buffer.reset(); name_buffer << f->get_name() << ".exp"; - func_decl * f_exp = m.mk_func_decl(symbol(name_buffer.c_str()), new_domain.size(), new_domain.c_ptr(), m_bv_util.mk_sort(m_util.get_ebits(f->get_range()))); - expr_ref a_sgn(m), a_sig(m), a_exp(m); - a_sgn = m.mk_app(f_sgn, new_args.size(), new_args.c_ptr()); - a_sig = m.mk_app(f_sig, new_args.size(), new_args.c_ptr()); - a_exp = m.mk_app(f_exp, new_args.size(), new_args.c_ptr()); - TRACE("fpa2bv_dbg", tout << "New UF func_decls : " << std::endl; - tout << mk_ismt2_pp(f_sgn, m) << std::endl; - tout << mk_ismt2_pp(f_sig, m) << std::endl; - tout << mk_ismt2_pp(f_exp, m) << std::endl; ); - m_uf23bvuf.insert(f, func_decl_triple(f_sgn, f_sig, f_exp)); - m.inc_ref(f); - m.inc_ref(f_sgn); - m.inc_ref(f_sig); - m.inc_ref(f_exp); - mk_fp(a_sgn, a_exp, a_sig, result); - } - } + new_args.push_back(args[i]); + } - TRACE("fpa2bv_dbg", tout << "UF result: " << mk_ismt2_pp(result, m) << std::endl; ); + func_decl * fd; + if (m_uf2bvuf.find(f, fd)) + mk_uninterpreted_output(f->get_range(), fd, new_args, result); + else { + sort_ref_buffer new_domain(m); + + for (unsigned i = 0; i < f->get_arity(); i++) { + sort * di = f->get_domain()[i]; + if (is_float(di)) + new_domain.push_back(m_bv_util.mk_sort(m_util.get_sbits(di) + m_util.get_ebits(di))); + else if (is_rm(di)) + new_domain.push_back(m_bv_util.mk_sort(3)); + else + new_domain.push_back(di); + } + + sort * orig_rng = f->get_range(); + sort_ref rng(orig_rng, m); + if (m_util.is_float(orig_rng)) + rng = m_bv_util.mk_sort(m_util.get_ebits(orig_rng) + m_util.get_sbits(orig_rng)); + else if (m_util.is_rm(orig_rng)) + rng = m_bv_util.mk_sort(3); + + func_decl_ref fbv(m); + fbv = m.mk_fresh_func_decl(new_domain.size(), new_domain.c_ptr(), rng); + TRACE("fpa2bv", tout << "New UF func_decl : " << mk_ismt2_pp(fbv, m) << std::endl;); + + m_uf2bvuf.insert(f, fbv); + m.inc_ref(f); + m.inc_ref(fbv); + + mk_uninterpreted_output(f->get_range(), fbv, new_args, result); + } + + TRACE("fpa2bv", tout << "UF result: " << mk_ismt2_pp(result, m) << std::endl; ); SASSERT(is_well_sorted(m, result)); } @@ -359,6 +374,17 @@ void fpa2bv_converter::mk_pzero(func_decl *f, expr_ref & result) { result); } +void fpa2bv_converter::mk_one(func_decl *f, expr_ref sign, expr_ref & result) { + sort * srt = f->get_range(); + SASSERT(is_float(srt)); + unsigned sbits = m_util.get_sbits(srt); + unsigned ebits = m_util.get_ebits(srt); + mk_fp(sign, + m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits), + m_bv_util.mk_numeral(0, sbits-1), + result); +} + void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, expr_ref & rm, expr_ref & c_sgn, expr_ref & c_sig, expr_ref & c_exp, expr_ref & d_sgn, expr_ref & d_sig, expr_ref & d_exp, expr_ref & res_sgn, expr_ref & res_sig, expr_ref & res_exp) @@ -828,7 +854,7 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, mk_is_ninf(y, c5); mk_ite(x_is_inf, nan, xy_zero, v5); - // (y is 0) -> if (x is 0) then NaN else inf with xor sign. + // (y is 0) -> if (x is 0) then NaN else inf with xor sign. c6 = y_is_zero; expr_ref sgn_inf(m); mk_ite(signs_xor, ninf, pinf, sgn_inf); @@ -959,14 +985,14 @@ void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, c3 = y_is_inf; v3 = x; - // (x is 0) -> x - c4 = x_is_zero; - v4 = pzero; - // (y is 0) -> NaN. - c5 = y_is_zero; - v5 = nan; + c4 = y_is_zero; + v4 = nan; + // (x is 0) -> x + c5 = x_is_zero; + v5 = pzero; + // else the actual remainder. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); @@ -1007,9 +1033,11 @@ void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, m_bv_util.mk_numeral(0, 3)); res_exp = m_bv_util.mk_sign_extend(2, b_exp); + // CMW: Actual rounding is not necessary here, this is + // just convenience to get rid of the extra bits. expr_ref rm(m); - rm = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); - round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); + rm = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); @@ -1019,6 +1047,11 @@ void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); + expr_ref result_is_zero(m), zeros(m); + mk_is_zero(result, result_is_zero); + mk_ite(x_is_pos, pzero, nzero, zeros); + mk_ite(result_is_zero, zeros, result, result); + SASSERT(is_well_sorted(m, result)); TRACE("fpa2bv_rem", tout << "REM = " << mk_ismt2_pp(result, m) << std::endl; ); @@ -1041,80 +1074,62 @@ void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); - expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), c1_and(m); + expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), both_zero(m), pzero(m); mk_is_zero(x, x_is_zero); mk_is_zero(y, y_is_zero); - m_simp.mk_and(x_is_zero, y_is_zero, c1_and); - mk_is_nan(x, x_is_nan); - m_simp.mk_or(x_is_nan, c1_and, c1); - + m_simp.mk_and(x_is_zero, y_is_zero, both_zero); + mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); - c2 = y_is_nan; + mk_pzero(f, pzero); + + expr_ref sgn_diff(m); + sgn_diff = m.mk_not(m.mk_eq(x_sgn, y_sgn)); + + expr_ref lt(m); + mk_float_lt(f, num, args, lt); - expr_ref c3(m); - mk_float_lt(f, num, args, c3); + result = y; + mk_ite(lt, x, result, result); + mk_ite(both_zero, y, result, result); + mk_ite(m.mk_and(both_zero, sgn_diff), pzero, result, result); // min(-0.0, +0.0) = min(+0.0, -0.0) = +0.0 + mk_ite(y_is_nan, x, result, result); + mk_ite(x_is_nan, y, result, result); - expr_ref r_sgn(m), r_sig(m), r_exp(m); - - expr_ref c3xy(m), c2c3(m); - m_simp.mk_ite(c3, x_sgn, y_sgn, c3xy); - m_simp.mk_ite(c2, x_sgn, c3xy, c2c3); - m_simp.mk_ite(c1, y_sgn, c2c3, r_sgn); - - expr_ref c3xy_sig(m), c2c3_sig(m); - m_simp.mk_ite(c3, x_sig, y_sig, c3xy_sig); - m_simp.mk_ite(c2, x_sig, c3xy_sig, c2c3_sig); - m_simp.mk_ite(c1, y_sig, c2c3_sig, r_sig); - - expr_ref c3xy_exp(m), c2c3_exp(m); - m_simp.mk_ite(c3, x_exp, y_exp, c3xy_exp); - m_simp.mk_ite(c2, x_exp, c3xy_exp, c2c3_exp); - m_simp.mk_ite(c1, y_exp, c2c3_exp, r_exp); - - mk_fp(r_sgn, r_exp, r_sig, result); + SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); - - expr * x = args[0], * y = args[1]; - expr * x_sgn, * x_sig, * x_exp; - expr * y_sgn, * y_sig, * y_exp; + expr * x = args[0], *y = args[1]; + + expr * x_sgn, *x_sig, *x_exp; + expr * y_sgn, *y_sig, *y_exp; split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); - expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), y_is_zero(m), x_is_zero(m), c1_and(m); - mk_is_zero(y, y_is_zero); + expr_ref x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m), both_zero(m), pzero(m); mk_is_zero(x, x_is_zero); - m_simp.mk_and(y_is_zero, x_is_zero, c1_and); - mk_is_nan(x, x_is_nan); - m_simp.mk_or(x_is_nan, c1_and, c1); - + mk_is_zero(y, y_is_zero); + m_simp.mk_and(x_is_zero, y_is_zero, both_zero); + mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); - c2 = y_is_nan; - - expr_ref c3(m); - mk_float_gt(f, num, args, c3); + mk_pzero(f, pzero); - expr_ref r_sgn(m), r_sig(m), r_exp(m); - - expr_ref c3xy_sgn(m), c2c3_sgn(m); - m_simp.mk_ite(c3, x_sgn, y_sgn, c3xy_sgn); - m_simp.mk_ite(c2, x_sgn, c3xy_sgn, c2c3_sgn); - m_simp.mk_ite(c1, y_sgn, c2c3_sgn, r_sgn); + expr_ref sgn_diff(m); + sgn_diff = m.mk_not(m.mk_eq(x_sgn, y_sgn)); - expr_ref c3xy_sig(m), c2c3_sig(m); - m_simp.mk_ite(c3, x_sig, y_sig, c3xy_sig); - m_simp.mk_ite(c2, x_sig, c3xy_sig, c2c3_sig); - m_simp.mk_ite(c1, y_sig, c2c3_sig, r_sig); + expr_ref gt(m); + mk_float_gt(f, num, args, gt); - expr_ref c3xy_exp(m), c2c3_exp(m); - m_simp.mk_ite(c3, x_exp, y_exp, c3xy_exp); - m_simp.mk_ite(c2, x_exp, c3xy_exp, c2c3_exp); - m_simp.mk_ite(c1, y_exp, c2c3_exp, r_exp); + result = y; + mk_ite(gt, x, result, result); + mk_ite(both_zero, y, result, result); + mk_ite(m.mk_and(both_zero, sgn_diff), pzero, result, result); // max(-0.0, +0.0) = max(+0.0, -0.0) = +0.0 + mk_ite(y_is_nan, x, result, result); + mk_ite(x_is_nan, y, result, result); - mk_fp(r_sgn, r_exp, r_sig, result); + SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1391,14 +1406,20 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); - sticky_raw = m_bv_util.mk_extract(sbits-5, 0, sig_abs); - sticky = m_bv_util.mk_zero_extend(sbits+3, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); + if (sbits > 5) { + sticky_raw = m_bv_util.mk_extract(sbits - 5, 0, sig_abs); + sticky = m_bv_util.mk_zero_extend(sbits + 3, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); + expr * res_or_args[2] = { m_bv_util.mk_extract(2 * sbits - 1, sbits - 4, sig_abs), sticky }; + res_sig = m_bv_util.mk_bv_or(2, res_or_args); + } + else { + unsigned too_short = 6 - sbits; + sig_abs = m_bv_util.mk_concat(sig_abs, m_bv_util.mk_numeral(0, too_short)); + res_sig = m_bv_util.mk_extract(sbits + 3, 0, sig_abs); + } dbg_decouple("fpa2bv_fma_add_sum_sticky", sticky); + SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); - expr * res_or_args[2] = { m_bv_util.mk_extract(2*sbits-1, sbits-4, sig_abs), sticky }; - res_sig = m_bv_util.mk_bv_or(2, res_or_args); - SASSERT(m_bv_util.get_bv_size(res_sig) == sbits+4); - expr_ref is_zero_sig(m), nil_sbits4(m); nil_sbits4 = m_bv_util.mk_numeral(0, sbits+4); m_simp.mk_eq(res_sig, nil_sbits4, is_zero_sig); @@ -1582,86 +1603,193 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * rm = args[0]; x = args[1]; + expr_ref rm_is_rta(m), rm_is_rte(m), rm_is_rtp(m), rm_is_rtn(m), rm_is_rtz(m); + mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_rta); + mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_rte); + mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_rtp); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_rtn); + mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_rtz); + expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); mk_nzero(f, nzero); - mk_pzero(f, pzero); + mk_pzero(f, pzero); - expr_ref x_is_zero(m), x_is_pos(m); + expr_ref x_is_zero(m), x_is_pos(m), x_is_neg(m); mk_is_zero(x, x_is_zero); mk_is_pos(x, x_is_pos); + mk_is_neg(x, x_is_neg); dbg_decouple("fpa2bv_r2i_x_is_zero", x_is_zero); dbg_decouple("fpa2bv_r2i_x_is_pos", x_is_pos); - expr_ref c1(m), c2(m), c3(m), c4(m); - expr_ref v1(m), v2(m), v3(m), v4(m), v5(m); + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m); + // (x is NaN) -> NaN mk_is_nan(x, c1); v1 = nan; + // (x is +-oo) -> x mk_is_inf(x, c2); v2 = x; + + // (x is +-0) -> x ; -0.0 -> -0.0, says IEEE754, Sec 5.9. + mk_is_zero(x, c3); + v3 = x; + + + expr_ref one_1(m), zero_1(m); + one_1 = m_bv_util.mk_numeral(1, 1); + zero_1 = m_bv_util.mk_numeral(0, 1); unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); - + dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); dbg_decouple("fpa2bv_r2i_unpacked_exp", a_exp); - expr_ref exp_is_small(m), exp_h(m), one_1(m); - exp_h = m_bv_util.mk_extract(ebits-1, ebits-1, a_exp); - one_1 = m_bv_util.mk_numeral(1, 1); - m_simp.mk_eq(exp_h, one_1, exp_is_small); - dbg_decouple("fpa2bv_r2i_exp_is_small", exp_is_small); - c3 = exp_is_small; - mk_ite(x_is_pos, pzero, nzero, v3); + expr_ref xzero(m); + mk_ite(m.mk_eq(a_sgn, one_1), nzero, pzero, xzero); + + // exponent < 0 -> 0/1 + expr_ref exp_lt_zero(m), exp_h(m); + exp_h = m_bv_util.mk_extract(ebits-1, ebits-1, a_exp); + m_simp.mk_eq(exp_h, one_1, exp_lt_zero); + dbg_decouple("fpa2bv_r2i_exp_lt_zero", exp_lt_zero); + c4 = exp_lt_zero; + + expr_ref pone(m), none(m), xone(m), c421(m), c422(m), c423(m), t1(m), t2(m), tie(m), v42(m), exp_lt_m1(m); + mk_one(f, zero_1, pone); + mk_one(f, one_1, none); + mk_ite(m.mk_eq(a_sgn, one_1), none, pone, xone); + m_simp.mk_eq(a_sig, m_bv_util.mk_numeral(fu().fm().m_powers2(sbits-1), sbits), t1); + m_simp.mk_eq(a_exp, m_bv_util.mk_numeral(-1, ebits), t2); + m_simp.mk_and(t1, t2, tie); + dbg_decouple("fpa2bv_r2i_c42_tie", tie); + + m_simp.mk_and(tie, rm_is_rte, c421); + m_simp.mk_and(tie, rm_is_rta, c422); + c423 = m_bv_util.mk_sle(a_exp, m_bv_util.mk_numeral(-2, ebits)); + + dbg_decouple("fpa2bv_r2i_c421", c421); + dbg_decouple("fpa2bv_r2i_c422", c422); + dbg_decouple("fpa2bv_r2i_c423", c423); + + v42 = xone; + mk_ite(c423, xzero, v42, v42); + mk_ite(c422, xone, v42, v42); + mk_ite(c421, xzero, v42, v42); + + expr_ref v4_rtn(m), v4_rtp(m); + mk_ite(x_is_neg, nzero, pone, v4_rtp); + mk_ite(x_is_neg, none, pzero, v4_rtn); + + mk_ite(rm_is_rtp, v4_rtp, v42, v4); + mk_ite(rm_is_rtn, v4_rtn, v4, v4); + mk_ite(rm_is_rtz, xzero, v4, v4); + + SASSERT(is_well_sorted(m, v4)); + + // exponent >= sbits-1 expr_ref exp_is_large(m); exp_is_large = m_bv_util.mk_sle(m_bv_util.mk_numeral(sbits-1, ebits), a_exp); dbg_decouple("fpa2bv_r2i_exp_is_large", exp_is_large); - c4 = exp_is_large; - v4 = x; + c5 = exp_is_large; + v5 = x; + + // Actual conversion with rounding. + // x.exponent >= 0 && x.exponent < x.sbits - 1 - // The actual rounding. expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = a_sgn; - res_exp = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 2), a_exp); + res_exp = a_exp; - expr_ref shift(m), r_shifted(m), l_shifted(m); - shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits-1, ebits+1), - m_bv_util.mk_sign_extend(1, a_exp)); - if (sbits > (ebits+1)) - r_shifted = m_bv_util.mk_bv_lshr(a_sig, m_bv_util.mk_zero_extend(sbits-(ebits+1), shift)); - else if (sbits < (ebits+1)) - r_shifted = m_bv_util.mk_extract(ebits, ebits-sbits+1, m_bv_util.mk_bv_lshr(m_bv_util.mk_zero_extend(ebits+1-sbits, a_sig), shift)); - else // sbits == ebits+1 - r_shifted = m_bv_util.mk_bv_lshr(a_sig, shift); - SASSERT(is_well_sorted(m, r_shifted)); - SASSERT(m_bv_util.get_bv_size(r_shifted) == sbits); + expr_ref shift(m), rshift(m), div(m), rem(m); + shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits - 1, sbits + 1), + m_bv_util.mk_sign_extend(sbits - ebits + 1, a_exp)); + rshift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits, sbits + 1), shift); + div = m_bv_util.mk_bv_lshr(m_bv_util.mk_zero_extend(1, a_sig), shift); + rem = m_bv_util.mk_bv_lshr(m_bv_util.mk_bv_shl(m_bv_util.mk_zero_extend(1, a_sig), rshift), rshift); - if (sbits > (ebits+1)) - l_shifted = m_bv_util.mk_bv_shl(r_shifted, m_bv_util.mk_zero_extend(sbits-(ebits+1), shift)); - else if (sbits < (ebits+1)) - l_shifted = m_bv_util.mk_extract(ebits, ebits-sbits+1, m_bv_util.mk_bv_shl(m_bv_util.mk_zero_extend(ebits+1-sbits, r_shifted), shift)); - else // sbits == ebits+1 - l_shifted = m_bv_util.mk_bv_shl(r_shifted, shift); - SASSERT(is_well_sorted(m, l_shifted)); - SASSERT(m_bv_util.get_bv_size(l_shifted) == sbits); + SASSERT(is_well_sorted(m, div)); + SASSERT(is_well_sorted(m, rem)); + SASSERT(m_bv_util.get_bv_size(div) == sbits + 1); + SASSERT(m_bv_util.get_bv_size(rem) == sbits + 1); - res_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 1), - m_bv_util.mk_concat(l_shifted, - m_bv_util.mk_numeral(0, 3))); + dbg_decouple("fpa2bv_r2i_shift", shift); + dbg_decouple("fpa2bv_r2i_rshift", rshift); + dbg_decouple("fpa2bv_r2i_div", div); + dbg_decouple("fpa2bv_r2i_rem", rem); - SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); + expr_ref div_p1(m); + div_p1 = m_bv_util.mk_bv_add(div, m_bv_util.mk_numeral(1, sbits+1)); - round(f->get_range(), rm, res_sgn, res_sig, res_exp, v5); + expr_ref tie2(m), tie2_c(m), div_last(m), v51(m), rem_shl(m); + rem_shl = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits - 1, 0, rem), zero_1); + m_simp.mk_eq(rem_shl, + m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits+1), shift), + tie2); + div_last = m_bv_util.mk_extract(0, 0, div); + tie2_c = m.mk_or(m.mk_and(tie2, + m.mk_or(m.mk_and(rm_is_rte, m.mk_eq(div_last, one_1)), + m.mk_and(rm_is_rta, m.mk_eq(div_last, zero_1)))), + m.mk_xor(m.mk_eq(a_sgn, one_1), + m_bv_util.mk_sle(m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits + 1), shift), + rem_shl))); + m_simp.mk_ite(tie2_c, div_p1, div, v51); + + dbg_decouple("fpa2bv_r2i_v51", v51); + dbg_decouple("fpa2bv_r2i_tie2", tie2); + + SASSERT(is_well_sorted(m, tie2)); + SASSERT(is_well_sorted(m, tie2_c)); + SASSERT(is_well_sorted(m, v51)); + + expr_ref c521(m), v52(m); + m_simp.mk_not(m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits+1)), c521); + m_simp.mk_and(c521, m.mk_eq(res_sgn, zero_1), c521); + m_simp.mk_ite(c521, div_p1, div, v52); + + expr_ref c531(m), v53(m); + m_simp.mk_not(m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits+1)), c531); + m_simp.mk_and(c531, m.mk_eq(res_sgn, one_1), c531); + m_simp.mk_ite(c531, div_p1, div, v53); + + expr_ref c51(m), c52(m), c53(m); + c51 = m.mk_or(rm_is_rte, rm_is_rta); + c52 = rm_is_rtp; + c53 = rm_is_rtn; + + res_sig = div; + m_simp.mk_ite(c53, v53, res_sig, res_sig); + m_simp.mk_ite(c52, v52, res_sig, res_sig); + m_simp.mk_ite(c51, v51, res_sig, res_sig); + res_sig = m_bv_util.mk_concat(res_sig, m_bv_util.mk_numeral(0, 3)); // rounding bits are all 0. + + SASSERT(m_bv_util.get_bv_size(res_exp) == ebits); + SASSERT(m_bv_util.get_bv_size(shift) == sbits + 1); + + expr_ref e_shift(m); + e_shift = (ebits + 2 <= sbits + 1) ? m_bv_util.mk_extract(ebits + 1, 0, shift) : + m_bv_util.mk_sign_extend((ebits + 2) - (sbits + 1), shift); + SASSERT(m_bv_util.get_bv_size(e_shift) == ebits + 2); + res_exp = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(2, res_exp), e_shift); + + SASSERT(m_bv_util.get_bv_size(res_sgn) == 1); + SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); + SASSERT(m_bv_util.get_bv_size(res_exp) == ebits + 2); + + // CMW: We use the rounder for normalization. + round(f->get_range(), rm, res_sgn, res_sig, res_exp, v6); // And finally, we tie them together. - mk_ite(c4, v4, v5, result); + mk_ite(c5, v5, v6, result); + mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); mk_ite(c2, v2, result, result); mk_ite(c1, v1, result, result); @@ -1960,6 +2088,7 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); + dbg_decouple("fpa2bv_to_float_x_sgn", sgn); dbg_decouple("fpa2bv_to_float_x_sig", sig); dbg_decouple("fpa2bv_to_float_x_exp", exp); dbg_decouple("fpa2bv_to_float_lz", lz); @@ -1977,13 +2106,17 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * // make sure that sig has at least to_sbits + 3 res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits + 3 - from_sbits)); } - else if (from_sbits >(to_sbits + 3)) { + else if (from_sbits > (to_sbits + 3)) { // collapse the extra bits into a sticky bit. expr_ref sticky(m), low(m), high(m); - low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig); high = m_bv_util.mk_extract(from_sbits - 1, from_sbits - to_sbits - 2, sig); + SASSERT(m_bv_util.get_bv_size(high) == to_sbits + 2); + low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, low.get()); - res_sig = m_bv_util.mk_concat(high, sticky); + SASSERT(m_bv_util.get_bv_size(sticky) == 1); + dbg_decouple("fpa2bv_to_float_sticky", sticky); + res_sig = m_bv_util.mk_concat(high, sticky); + SASSERT(m_bv_util.get_bv_size(res_sig) == to_sbits + 3); } else res_sig = sig; @@ -1992,8 +2125,9 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * unsigned sig_sz = m_bv_util.get_bv_size(res_sig); SASSERT(sig_sz == to_sbits + 4); - expr_ref exponent_overflow(m); + expr_ref exponent_overflow(m), exponent_underflow(m); exponent_overflow = m.mk_false(); + exponent_underflow = m.mk_false(); if (from_ebits < (to_ebits + 2)) { res_exp = m_bv_util.mk_sign_extend(to_ebits - from_ebits + 2, exp); @@ -2003,37 +2137,58 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz); res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); } - else if (from_ebits >(to_ebits + 2)) { - expr_ref high(m), low(m), lows(m), high_red_or(m), high_red_and(m), h_or_eq(m), h_and_eq(m); - expr_ref no_ovf(m), zero1(m), s_is_one(m), s_is_zero(m); - high = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, exp); - low = m_bv_util.mk_extract(to_ebits + 1, 0, exp); - lows = m_bv_util.mk_extract(to_ebits + 1, to_ebits + 1, low); - - high_red_or = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, high.get()); - high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get()); - - zero1 = m_bv_util.mk_numeral(0, 1); - m_simp.mk_eq(high_red_and, one1, h_and_eq); - m_simp.mk_eq(high_red_or, zero1, h_or_eq); - m_simp.mk_eq(lows, zero1, s_is_zero); - m_simp.mk_eq(lows, one1, s_is_one); - - expr_ref c2(m); - m_simp.mk_ite(h_or_eq, s_is_one, m.mk_false(), c2); - m_simp.mk_ite(h_and_eq, s_is_zero, c2, exponent_overflow); - - // Note: Upon overflow, we _could_ try to shift the significand around... - - // subtract lz for subnormal numbers. - expr_ref lz_ext(m), lz_rest(m), lz_redor(m), lz_redor_bool(m); - lz_ext = m_bv_util.mk_extract(to_ebits + 1, 0, lz); + else if (from_ebits > (to_ebits + 2)) { + expr_ref lz_rest(m), lz_redor(m), lz_redor_bool(m); lz_rest = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, lz); lz_redor = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, lz_rest.get()); m_simp.mk_eq(lz_redor, one1, lz_redor_bool); - m_simp.mk_or(exponent_overflow, lz_redor_bool, exponent_overflow); + dbg_decouple("fpa2bv_to_float_exp_lz_redor", lz_redor); - res_exp = m_bv_util.mk_bv_sub(low, lz_ext); + // subtract lz for subnormal numbers. + expr_ref exp_sub_lz(m); + exp_sub_lz = m_bv_util.mk_bv_sub(exp, lz); + dbg_decouple("fpa2bv_to_float_exp_sub_lz", exp_sub_lz); + + expr_ref high(m), low(m), low_msb(m); + expr_ref no_ovf(m), zero1(m); + high = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, exp_sub_lz); + low = m_bv_util.mk_extract(to_ebits + 1, 0, exp_sub_lz); + low_msb = m_bv_util.mk_extract(to_ebits + 1, to_ebits + 1, low); + dbg_decouple("fpa2bv_to_float_exp_high", high); + dbg_decouple("fpa2bv_to_float_exp_low", low); + dbg_decouple("fpa2bv_to_float_exp_low_msb", low_msb); + + res_exp = low; + + expr_ref high_red_or(m), high_red_and(m); + high_red_or = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, high.get()); + high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get()); + + expr_ref h_or_eq_0(m), h_and_eq_1(m), low_msb_is_one(m), low_msb_is_zero(m); + zero1 = m_bv_util.mk_numeral(0, 1); + m_simp.mk_eq(high_red_and, one1, h_and_eq_1); + m_simp.mk_eq(high_red_or, zero1, h_or_eq_0); + m_simp.mk_eq(low_msb, zero1, low_msb_is_zero); + m_simp.mk_eq(low_msb, one1, low_msb_is_one); + dbg_decouple("fpa2bv_to_float_exp_h_and_eq_1", h_and_eq_1); + dbg_decouple("fpa2bv_to_float_exp_h_or_eq_0", h_or_eq_0); + dbg_decouple("fpa2bv_to_float_exp_s_is_zero", low_msb_is_zero); + dbg_decouple("fpa2bv_to_float_exp_s_is_one", low_msb_is_one); + + m_simp.mk_and(h_or_eq_0, low_msb_is_one, exponent_underflow); + m_simp.mk_and(h_and_eq_1, low_msb_is_zero, exponent_overflow); + m_simp.mk_or(exponent_overflow, lz_redor_bool, exponent_overflow); + dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow); + dbg_decouple("fpa2bv_to_float_exp_udf", exponent_underflow); + + // exponent underflow means that the result is the smallest + // representable float, rounded according to rm. + m_simp.mk_ite(exponent_underflow, + m_bv_util.mk_concat(m_bv_util.mk_numeral(1, 1), + m_bv_util.mk_numeral(1, to_ebits+1)), + res_exp, + res_exp); + m_simp.mk_ite(exponent_underflow, m_bv_util.mk_numeral(1, to_sbits+4), res_sig, res_sig); } else // from_ebits == (to_ebits + 2) res_exp = m_bv_util.mk_bv_sub(exp, lz); @@ -2052,8 +2207,7 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * m_simp.mk_eq(sgn, one1, is_neg); mk_ite(is_neg, ninf, pinf, sig_inf); - dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow); - mk_ite(exponent_overflow, sig_inf, rounded, v6); + mk_ite(exponent_overflow, sig_inf, rounded, v6); // And finally, we tie them together. mk_ite(c5, v5, v6, result); @@ -2082,13 +2236,13 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * SASSERT(sz == 3); BV_RM_VAL bv_rm = (BV_RM_VAL)tmp_rat.get_unsigned(); - mpf_rounding_mode rm; + mpf_rounding_mode mrm; switch (bv_rm) { - case BV_RM_TIES_TO_AWAY: rm = MPF_ROUND_NEAREST_TAWAY; break; - case BV_RM_TIES_TO_EVEN: rm = MPF_ROUND_NEAREST_TEVEN; break; - case BV_RM_TO_NEGATIVE: rm = MPF_ROUND_TOWARD_NEGATIVE; break; - case BV_RM_TO_POSITIVE: rm = MPF_ROUND_TOWARD_POSITIVE; break; - case BV_RM_TO_ZERO: rm = MPF_ROUND_TOWARD_ZERO; break; + case BV_RM_TIES_TO_AWAY: mrm = MPF_ROUND_NEAREST_TAWAY; break; + case BV_RM_TIES_TO_EVEN: mrm = MPF_ROUND_NEAREST_TEVEN; break; + case BV_RM_TO_NEGATIVE: mrm = MPF_ROUND_TOWARD_NEGATIVE; break; + case BV_RM_TO_POSITIVE: mrm = MPF_ROUND_TOWARD_POSITIVE; break; + case BV_RM_TO_ZERO: mrm = MPF_ROUND_TOWARD_ZERO; break; default: UNREACHABLE(); } @@ -2100,17 +2254,15 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * return mk_pzero(f, result); else { scoped_mpf v(m_mpf_manager); - m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); + m_util.fm().set(v, ebits, sbits, mrm, q.to_mpq()); - - - expr_ref sgn(m), s(m), e(m), unbiased_exp(m); + expr_ref sgn(m), sig(m), exp(m), unbiased_exp(m); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v)) ? 1 : 0, 1); - s = m_bv_util.mk_numeral(m_util.fm().sig(v), sbits - 1); + sig = m_bv_util.mk_numeral(m_util.fm().sig(v), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v), ebits); - mk_bias(unbiased_exp, e); + mk_bias(unbiased_exp, exp); - mk_fp(sgn, e, s, result); + mk_fp(sgn, exp, sig, result); } } else if (m_util.au().is_numeral(x)) { @@ -2138,44 +2290,45 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * expr_ref v1(m), v2(m), v3(m), v4(m); - expr_ref sgn(m), s(m), e(m), unbiased_exp(m); + expr_ref sgn(m), sig(m), exp(m), unbiased_exp(m); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_nta)) ? 1 : 0, 1); - s = m_bv_util.mk_numeral(m_util.fm().sig(v_nta), sbits - 1); + sig = m_bv_util.mk_numeral(m_util.fm().sig(v_nta), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_nta), ebits); - mk_bias(unbiased_exp, e); - mk_fp(sgn, e, s, v1); + mk_bias(unbiased_exp, exp); + mk_fp(sgn, exp, sig, v1); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_nte)) ? 1 : 0, 1); - s = m_bv_util.mk_numeral(m_util.fm().sig(v_nte), sbits - 1); + sig = m_bv_util.mk_numeral(m_util.fm().sig(v_nte), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_nte), ebits); - mk_bias(unbiased_exp, e); - mk_fp(sgn, e, s, v2); + mk_bias(unbiased_exp, exp); + mk_fp(sgn, exp, sig, v2); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 1); - s = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1); + sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tp), ebits); - mk_bias(unbiased_exp, e); - mk_fp(sgn, e, s, v3); + mk_bias(unbiased_exp, exp); + mk_fp(sgn, exp, sig, v3); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tn)) ? 1 : 0, 1); - s = m_bv_util.mk_numeral(m_util.fm().sig(v_tn), sbits - 1); + sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tn), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tn), ebits); - mk_bias(unbiased_exp, e); - mk_fp(sgn, e, s, v4); + mk_bias(unbiased_exp, exp); + mk_fp(sgn, exp, sig, v4); sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 1); - s = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1); + sig = m_bv_util.mk_numeral(m_util.fm().sig(v_tp), sbits - 1); unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tp), ebits); - mk_bias(unbiased_exp, e); + mk_bias(unbiased_exp, exp); - mk_fp(sgn, e, s, result); + mk_fp(sgn, exp, sig, result); mk_ite(rm_tn, v4, result, result); mk_ite(rm_tp, v3, result, result); mk_ite(rm_nte, v2, result, result); mk_ite(rm_nta, v1, result, result); } } - else { + else { + SASSERT(!m_arith_util.is_numeral(x)); bv_util & bu = m_bv_util; arith_util & au = m_arith_util; @@ -2193,12 +2346,8 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * expr_ref rme(rm, m); round(s, rme, sgn, sig, exp, result); - expr_ref c0(m); - mk_is_zero(x, c0); - mk_ite(c0, x, result, result); - expr * e = m.mk_eq(m_util.mk_to_real(result), x); - m_extra_assertions.push_back(e); + m_extra_assertions.push_back(e); } SASSERT(is_well_sorted(m, result)); @@ -2622,8 +2771,8 @@ void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * result = m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, e), s); } -void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - TRACE("fpa2bv_to_ubv", for (unsigned i = 0; i < num; i++) +void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) { + TRACE("fpa2bv_to_bv", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(f->get_num_parameters() == 1); @@ -2654,25 +2803,34 @@ void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * arg // NaN, Inf, or negative (except -0) -> unspecified expr_ref c1(m), v1(m); - c1 = m.mk_or(x_is_nan, x_is_inf, m.mk_and(x_is_neg, m.mk_not(x_is_nzero))); + if (!is_signed) + c1 = m.mk_or(x_is_nan, x_is_inf, m.mk_and(x_is_neg, m.mk_not(x_is_nzero))); + else + c1 = m.mk_or(x_is_nan, x_is_inf); v1 = mk_to_ubv_unspecified(bv_sz); + dbg_decouple("fpa2bv_to_bv_c1", c1); // +-Zero -> 0 expr_ref c2(m), v2(m); c2 = x_is_zero; v2 = m_bv_util.mk_numeral(rational(0), bv_srt); - dbg_decouple("fpa2bv_to_ubv_c2", c2); + dbg_decouple("fpa2bv_to_bv_c2", c2); // Otherwise... expr_ref sgn(m), sig(m), exp(m), lz(m); unpack(x, sgn, sig, exp, lz, true); + dbg_decouple("fpa2bv_to_bv_sgn", sgn); + dbg_decouple("fpa2bv_to_bv_sig", sig); + dbg_decouple("fpa2bv_to_bv_exp", exp); + dbg_decouple("fpa2bv_to_bv_lz", lz); + // sig is of the form +- [1].[sig] * 2^(exp-lz) SASSERT(m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); - SASSERT(m_bv_util.get_bv_size(lz) == ebits); - dbg_decouple("fpa2bv_to_ubv_sig", sig); + SASSERT(m_bv_util.get_bv_size(lz) == ebits); + unsigned sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz == sbits); if (sig_sz < (bv_sz + 3)) @@ -2680,62 +2838,73 @@ void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * arg sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz >= (bv_sz + 3)); - expr_ref exp_m_lz(m), shift(m), shift_neg(m), bv0_e2(m), shift_abs(m); + expr_ref exp_m_lz(m), e_m_lz_m_bv_sz(m), shift(m), bv0_e2(m), shift_abs(m); exp_m_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), m_bv_util.mk_zero_extend(2, lz)); - shift = m_bv_util.mk_bv_sub(exp_m_lz, - m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); - shift_neg = m_bv_util.mk_bv_neg(shift); + e_m_lz_m_bv_sz = m_bv_util.mk_bv_sub(exp_m_lz, + m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); + shift = m_bv_util.mk_bv_neg(e_m_lz_m_bv_sz); bv0_e2 = m_bv_util.mk_numeral(0, ebits + 2); - shift_abs = m.mk_ite(m_bv_util.mk_sle(shift, bv0_e2), shift_neg, shift); + shift_abs = m.mk_ite(m_bv_util.mk_sle(shift, bv0_e2), e_m_lz_m_bv_sz, shift); SASSERT(m_bv_util.get_bv_size(shift) == ebits + 2); - SASSERT(m_bv_util.get_bv_size(shift_neg) == ebits + 2); SASSERT(m_bv_util.get_bv_size(shift_abs) == ebits + 2); - dbg_decouple("fpa2bv_to_ubv_shift", shift); - dbg_decouple("fpa2bv_to_ubv_shift_abs", shift_abs); + dbg_decouple("fpa2bv_to_bv_shift", shift); + dbg_decouple("fpa2bv_to_bv_shift_abs", shift_abs); // x is of the form +- [1].[sig][r][g][s] ... and at least bv_sz + 3 long // [1][ ... sig ... ][r][g][ ... s ...] // [ ... ubv ... ][r][g][ ... s ... ] - expr_ref max_shift(m); - max_shift = m_bv_util.mk_numeral(sig_sz, sig_sz); + shift_abs = m_bv_util.mk_zero_extend(sig_sz - ebits - 2, shift_abs); SASSERT(m_bv_util.get_bv_size(shift_abs) == sig_sz); expr_ref c_in_limits(m); - c_in_limits = m_bv_util.mk_sle(shift, m_bv_util.mk_numeral(0, ebits + 2)); - dbg_decouple("fpa2bv_to_ubv_in_limits", c_in_limits); + if (!is_signed) + c_in_limits = m_bv_util.mk_sle(bv0_e2, shift); + else + c_in_limits = m.mk_or(m_bv_util.mk_sle(m_bv_util.mk_numeral(1, ebits + 2), shift), + m.mk_and(m.mk_eq(m_bv_util.mk_numeral(0, ebits + 2), shift), + m.mk_eq(sig, m_bv_util.mk_concat(bv1, m_bv_util.mk_numeral(0, sig_sz-1))))); + dbg_decouple("fpa2bv_to_bv_in_limits", c_in_limits); - expr_ref shifted_sig(m); - shifted_sig = m_bv_util.mk_bv_lshr(sig, shift_abs); - dbg_decouple("fpa2bv_to_ubv_shifted_sig", shifted_sig); + expr_ref r_shifted_sig(m), l_shifted_sig(m); + r_shifted_sig = m_bv_util.mk_bv_lshr(sig, shift_abs); + l_shifted_sig = m_bv_util.mk_bv_shl(sig, m_bv_util.mk_bv_sub( + m_bv_util.mk_numeral(m_bv_util.get_bv_size(sig), m_bv_util.get_bv_size(sig)), + shift_abs)); + dbg_decouple("fpa2bv_to_bv_r_shifted_sig", r_shifted_sig); + dbg_decouple("fpa2bv_to_bv_l_shifted_sig", l_shifted_sig); expr_ref last(m), round(m), sticky(m); - last = m_bv_util.mk_extract(sig_sz - bv_sz - 0, sig_sz - bv_sz - 0, shifted_sig); - round = m_bv_util.mk_extract(sig_sz - bv_sz - 1, sig_sz - bv_sz - 1, shifted_sig); - sticky = m.mk_ite(m.mk_eq(m_bv_util.mk_extract(sig_sz - bv_sz - 2, 0, shifted_sig), - m_bv_util.mk_numeral(0, sig_sz - (bv_sz + 3) + 2)), - bv0, - bv1); - dbg_decouple("fpa2bv_to_ubv_last", last); - dbg_decouple("fpa2bv_to_ubv_round", round); - dbg_decouple("fpa2bv_to_ubv_sticky", sticky); + last = m_bv_util.mk_extract(sig_sz - bv_sz - 0, sig_sz - bv_sz - 0, r_shifted_sig); + round = m_bv_util.mk_extract(sig_sz - bv_sz - 1, sig_sz - bv_sz - 1, r_shifted_sig); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, l_shifted_sig.get()); + dbg_decouple("fpa2bv_to_bv_last", last); + dbg_decouple("fpa2bv_to_bv_round", round); + dbg_decouple("fpa2bv_to_bv_sticky", sticky); expr_ref rounding_decision(m); rounding_decision = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(rounding_decision) == 1); - dbg_decouple("fpa2bv_to_ubv_rounding_decision", rounding_decision); + dbg_decouple("fpa2bv_to_bv_rounding_decision", rounding_decision); expr_ref unrounded_sig(m), pre_rounded(m), inc(m); - unrounded_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_extract(sig_sz - 1, sig_sz - bv_sz, shifted_sig)); + unrounded_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_extract(sig_sz - 1, sig_sz - bv_sz, r_shifted_sig)); inc = m_bv_util.mk_zero_extend(1, m_bv_util.mk_zero_extend(bv_sz - 1, rounding_decision)); pre_rounded = m_bv_util.mk_bv_add(unrounded_sig, inc); + dbg_decouple("fpa2bv_to_bv_inc", inc); + dbg_decouple("fpa2bv_to_bv_pre_rounded", pre_rounded); expr_ref rnd_overflow(m), rnd(m), rnd_has_overflown(m); rnd_overflow = m_bv_util.mk_extract(bv_sz, bv_sz, pre_rounded); rnd = m_bv_util.mk_extract(bv_sz - 1, 0, pre_rounded); rnd_has_overflown = m.mk_eq(rnd_overflow, bv1); - dbg_decouple("fpa2bv_to_ubv_rnd_has_overflown", rnd_has_overflown); + dbg_decouple("fpa2bv_to_bv_rnd_has_overflown", rnd_has_overflown); + + if (is_signed) + rnd = m.mk_ite(m.mk_eq(sgn, bv1), m_bv_util.mk_bv_neg(rnd), rnd); + + dbg_decouple("fpa2bv_to_bv_rnd", rnd); result = m.mk_ite(rnd_has_overflown, mk_to_ubv_unspecified(bv_sz), rnd); result = m.mk_ite(c_in_limits, result, mk_to_ubv_unspecified(bv_sz)); @@ -2745,144 +2914,16 @@ void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * arg SASSERT(is_well_sorted(m, result)); } +void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + TRACE("fpa2bv_to_ubv", for (unsigned i = 0; i < num; i++) + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + mk_to_bv(f, num, args, false, result); +} + void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_sbv", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); - - SASSERT(f->get_num_parameters() == 1); - SASSERT(f->get_parameter(0).is_int()); - SASSERT(num == 2); - SASSERT(m_bv_util.get_bv_size(args[0]) == 3); - SASSERT(m_util.is_float(args[1])); - - expr * rm = args[0]; - expr * x = args[1]; - sort * xs = m.get_sort(x); - sort * bv_srt = f->get_range(); - - unsigned ebits = m_util.get_ebits(xs); - unsigned sbits = m_util.get_sbits(xs); - unsigned bv_sz = (unsigned)f->get_parameter(0).get_int(); - - expr_ref bv0(m), bv1(m), bv0_2(m), bv1_2(m), bv3_2(m); - bv0 = m_bv_util.mk_numeral(0, 1); - bv1 = m_bv_util.mk_numeral(1, 1); - bv0_2 = m_bv_util.mk_numeral(0, 2); - bv1_2 = m_bv_util.mk_numeral(1, 2); - bv3_2 = m_bv_util.mk_numeral(3, 2); - - expr_ref x_is_nan(m), x_is_inf(m), x_is_zero(m), x_is_neg(m), x_is_nzero(m); - mk_is_nan(x, x_is_nan); - mk_is_inf(x, x_is_inf); - mk_is_zero(x, x_is_zero); - mk_is_neg(x, x_is_neg); - mk_is_nzero(x, x_is_nzero); - - // NaN, Inf -> unspecified - expr_ref c1(m), v1(m); - c1 = m.mk_or(x_is_nan, x_is_inf); - v1 = mk_to_sbv_unspecified(bv_sz); - dbg_decouple("fpa2bv_to_sbv_c1", c1); - - // +-Zero -> 0 - expr_ref c2(m), v2(m); - c2 = x_is_zero; - v2 = m_bv_util.mk_numeral(rational(0), bv_srt); - dbg_decouple("fpa2bv_to_sbv_c2", c2); - - // Otherwise... - expr_ref sgn(m), sig(m), exp(m), lz(m); - unpack(x, sgn, sig, exp, lz, true); - - dbg_decouple("fpa2bv_to_sbv_sgn", sgn); - dbg_decouple("fpa2bv_to_sbv_sig", sig); - dbg_decouple("fpa2bv_to_sbv_exp", exp); - - // x is of the form +- [1].[sig] * 2^(exp-lz) - SASSERT(m_bv_util.get_bv_size(sgn) == 1); - SASSERT(m_bv_util.get_bv_size(sig) == sbits); - SASSERT(m_bv_util.get_bv_size(exp) == ebits); - SASSERT(m_bv_util.get_bv_size(lz) == ebits); - dbg_decouple("fpa2bv_to_sbv_sig", sig); - unsigned sig_sz = m_bv_util.get_bv_size(sig); - SASSERT(sig_sz == sbits); - if (sig_sz < (bv_sz + 3)) - sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, bv_sz - sig_sz + 3)); - sig_sz = m_bv_util.get_bv_size(sig); - SASSERT(sig_sz >= (bv_sz + 3)); - - expr_ref exp_m_lz(m), shift(m), shift_neg(m), bv0_e2(m), shift_abs(m); - exp_m_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), - m_bv_util.mk_zero_extend(2, lz)); - shift = m_bv_util.mk_bv_sub(exp_m_lz, - m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); - shift_neg = m_bv_util.mk_bv_neg(shift); - bv0_e2 = m_bv_util.mk_numeral(0, ebits + 2); - shift_abs = m.mk_ite(m_bv_util.mk_sle(shift, bv0_e2), shift_neg, shift); - SASSERT(m_bv_util.get_bv_size(shift) == ebits + 2); - SASSERT(m_bv_util.get_bv_size(shift_neg) == ebits + 2); - SASSERT(m_bv_util.get_bv_size(shift_abs) == ebits + 2); - dbg_decouple("fpa2bv_to_sbv_shift", shift); - dbg_decouple("fpa2bv_to_sbv_shift_abs", shift_abs); - - // sig is of the form +- [1].[sig][r][g][s] ... and at least bv_sz + 3 long - // [1][ ... sig ... ][r][g][ ... s ...] - // [ ... ubv ... ][r][g][ ... s ... ] - expr_ref max_shift(m); - max_shift = m_bv_util.mk_numeral(sig_sz, sig_sz); - shift_abs = m_bv_util.mk_zero_extend(sig_sz - ebits - 2, shift_abs); - SASSERT(m_bv_util.get_bv_size(shift_abs) == sig_sz); - - expr_ref c_in_limits(m); - c_in_limits = m_bv_util.mk_sle(shift, m_bv_util.mk_numeral(0, ebits + 2)); - dbg_decouple("fpa2bv_to_sbv_in_limits", c_in_limits); - - expr_ref shifted_sig(m); - shifted_sig = m_bv_util.mk_bv_lshr(sig, shift_abs); - dbg_decouple("fpa2bv_to_sbv_shifted_sig", shifted_sig); - - expr_ref last(m), round(m), sticky(m); - last = m_bv_util.mk_extract(sig_sz - bv_sz - 0, sig_sz - bv_sz - 0, shifted_sig); - round = m_bv_util.mk_extract(sig_sz - bv_sz - 1, sig_sz - bv_sz - 1, shifted_sig); - sticky = m.mk_ite(m.mk_eq(m_bv_util.mk_extract(sig_sz - bv_sz - 2, 0, shifted_sig), - m_bv_util.mk_numeral(0, sig_sz - (bv_sz + 3) + 2)), - bv0, - bv1); - dbg_decouple("fpa2bv_to_sbv_last", last); - dbg_decouple("fpa2bv_to_sbv_round", round); - dbg_decouple("fpa2bv_to_sbv_sticky", sticky); - - expr_ref rounding_decision(m); - rounding_decision = mk_rounding_decision(rm, sgn, last, round, sticky); - SASSERT(m_bv_util.get_bv_size(rounding_decision) == 1); - dbg_decouple("fpa2bv_to_sbv_rounding_decision", rounding_decision); - - expr_ref unrounded_sig(m), pre_rounded(m), inc(m); - unrounded_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_extract(sig_sz - 1, sig_sz - bv_sz, shifted_sig)); - inc = m_bv_util.mk_zero_extend(1, m_bv_util.mk_zero_extend(bv_sz - 1, rounding_decision)); - pre_rounded = m_bv_util.mk_bv_add(unrounded_sig, inc); - dbg_decouple("fpa2bv_to_sbv_inc", inc); - dbg_decouple("fpa2bv_to_sbv_unrounded_sig", unrounded_sig); - dbg_decouple("fpa2bv_to_sbv_pre_rounded", pre_rounded); - - expr_ref rnd_overflow(m), rnd_abs(m), rnd_signed(m), rnd_has_overflown(m), extra_neg(m); - rnd_overflow = m_bv_util.mk_extract(bv_sz, bv_sz - 1, pre_rounded); - rnd_abs = m_bv_util.mk_extract(bv_sz - 1, 0, pre_rounded); - rnd_signed = m.mk_ite(m.mk_eq(sgn, bv1), m_bv_util.mk_bv_neg(rnd_abs), rnd_abs); - extra_neg = m_bv_util.mk_numeral(fu().fm().m_powers2(bv_sz-1), bv_sz+1); - rnd_has_overflown = m.mk_and(m.mk_not(m.mk_eq(rnd_overflow, bv0_2)), - m.mk_not(m.mk_and(m.mk_eq(sgn, bv1), m.mk_eq(pre_rounded, extra_neg)))); - dbg_decouple("fpa2bv_to_sbv_extra_neg", extra_neg); - dbg_decouple("fpa2bv_to_sbv_rnd_overflow", rnd_overflow); - dbg_decouple("fpa2bv_to_sbv_rnd_abs", rnd_abs); - dbg_decouple("fpa2bv_to_sbv_rnd_has_overflown", rnd_has_overflown); - - result = m.mk_ite(rnd_has_overflown, mk_to_sbv_unspecified(bv_sz), rnd_signed); - result = m.mk_ite(c_in_limits, result, mk_to_sbv_unspecified(bv_sz)); - result = m.mk_ite(c2, v2, result); - result = m.mk_ite(c1, v1, result); - - SASSERT(is_well_sorted(m, result)); + mk_to_bv(f, num, args, true, result); } expr_ref fpa2bv_converter::mk_to_ubv_unspecified(unsigned width) { @@ -3046,14 +3087,16 @@ void fpa2bv_converter::mk_is_normal(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split_fp(e, sgn, exp, sig); - expr_ref is_special(m), is_denormal(m), p(m); + expr_ref is_special(m), is_denormal(m), p(m), is_zero(m); mk_is_denormal(e, is_denormal); + mk_is_zero(e, is_zero); unsigned ebits = m_bv_util.get_bv_size(exp); p = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits), ebits); m_simp.mk_eq(exp, p, is_special); expr_ref or_ex(m); m_simp.mk_or(is_special, is_denormal, or_ex); + m_simp.mk_or(is_zero, or_ex, or_ex); m_simp.mk_not(or_ex, result); } @@ -3147,7 +3190,7 @@ void fpa2bv_converter::mk_unbias(expr * e, expr_ref & result) { expr_ref e_plus_one(m); e_plus_one = m_bv_util.mk_bv_add(e, m_bv_util.mk_numeral(1, ebits)); - + expr_ref leading(m), n_leading(m), rest(m); leading = m_bv_util.mk_extract(ebits-1, ebits-1, e_plus_one); n_leading = m_bv_util.mk_bv_not(leading); @@ -3171,6 +3214,10 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref SASSERT(m_bv_util.get_bv_size(exp) == ebits); SASSERT(m_bv_util.get_bv_size(sig) == sbits-1); + dbg_decouple("fpa2bv_unpack_sgn", sgn); + dbg_decouple("fpa2bv_unpack_exp", exp); + dbg_decouple("fpa2bv_unpack_sig", sig); + expr_ref is_normal(m); mk_is_normal(e, is_normal); @@ -3180,8 +3227,7 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref dbg_decouple("fpa2bv_unpack_normal_exp", normal_exp); expr_ref denormal_sig(m), denormal_exp(m); - denormal_sig = m_bv_util.mk_zero_extend(1, sig); - // SASSERT(ebits >= 3); // Note: when ebits=2 there is no 1-exponent, so mk_unbias will produce a 0. + denormal_sig = m_bv_util.mk_zero_extend(1, sig); denormal_exp = m_bv_util.mk_numeral(1, ebits); mk_unbias(denormal_exp, denormal_exp); dbg_decouple("fpa2bv_unpack_denormal_exp", denormal_exp); @@ -3214,7 +3260,7 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref // the maximum shift is `sbits', because after that the mantissa // would be zero anyways. So we can safely cut the shift variable down, // as long as we check the higher bits. - expr_ref sh(m), is_sh_zero(m), sl(m), zero_s(m), sbits_s(m), short_shift(m); + expr_ref sh(m), is_sh_zero(m), sl(m), sbits_s(m), short_shift(m); zero_s = m_bv_util.mk_numeral(0, sbits-1); sbits_s = m_bv_util.mk_numeral(sbits, sbits); sh = m_bv_util.mk_extract(ebits-1, sbits, shift); @@ -3275,6 +3321,17 @@ void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { } expr_ref fpa2bv_converter::mk_rounding_decision(expr * rm, expr * sgn, expr * last, expr * round, expr * sticky) { + expr_ref rmr(rm, m); + expr_ref sgnr(sgn, m); + expr_ref lastr(last, m); + expr_ref roundr(round, m); + expr_ref stickyr(sticky, m); + dbg_decouple("fpa2bv_rnd_dec_rm", rmr); + dbg_decouple("fpa2bv_rnd_dec_sgn", sgnr); + dbg_decouple("fpa2bv_rnd_dec_last", lastr); + dbg_decouple("fpa2bv_rnd_dec_round", roundr); + dbg_decouple("fpa2bv_rnd_dec_sticky", stickyr); + expr_ref last_or_sticky(m), round_or_sticky(m), not_last(m), not_round(m), not_sticky(m), not_lors(m), not_rors(m), not_sgn(m); expr * last_sticky[2] = { last, sticky }; expr * round_sticky[2] = { round, sticky }; @@ -3312,6 +3369,7 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * rm, expr * sgn, expr * la m_simp.mk_ite(rm_is_away, inc_taway, inc_c3, inc_c2); m_simp.mk_ite(rm_is_even, inc_teven, inc_c2, res); + dbg_decouple("fpa2bv_rnd_dec_res", res); return res; } @@ -3637,15 +3695,5 @@ void fpa2bv_converter::reset(void) { dec_ref_map_key_values(m, m_rm_const2bv); dec_ref_map_key_values(m, m_uf2bvuf); - obj_map::iterator it = m_uf23bvuf.begin(); - obj_map::iterator end = m_uf23bvuf.end(); - for (; it != end; ++it) { - m.dec_ref(it->m_key); - m.dec_ref(it->m_value.f_sgn); - m.dec_ref(it->m_value.f_sig); - m.dec_ref(it->m_value.f_exp); - } - m_uf23bvuf.reset(); - m_extra_assertions.reset(); } diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index fa797b610..b91ced404 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _FPA2BV_CONVERTER_ -#define _FPA2BV_CONVERTER_ +#ifndef FPA2BV_CONVERTER_H_ +#define FPA2BV_CONVERTER_H_ #include"ast.h" #include"obj_hashtable.h" @@ -55,8 +55,7 @@ protected: obj_map m_const2bv; obj_map m_rm_const2bv; - obj_map m_uf2bvuf; - obj_map m_uf23bvuf; + obj_map m_uf2bvuf; public: fpa2bv_converter(ast_manager & m); @@ -80,19 +79,20 @@ public: void mk_eq(expr * a, expr * b, expr_ref & result); void mk_ite(expr * c, expr * t, expr * f, expr_ref & result); + void mk_distinct(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_rounding_mode(func_decl * f, expr_ref & result); void mk_numeral(func_decl * f, unsigned num, expr * const * args, expr_ref & result); virtual void mk_const(func_decl * f, expr_ref & result); virtual void mk_rm_const(func_decl * f, expr_ref & result); - void mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + virtual void mk_uninterpreted_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_var(unsigned base_inx, sort * srt, expr_ref & result); void mk_pinf(func_decl * f, expr_ref & result); void mk_ninf(func_decl * f, expr_ref & result); void mk_nan(func_decl * f, expr_ref & result); void mk_nzero(func_decl *f, expr_ref & result); - void mk_pzero(func_decl *f, expr_ref & result); + void mk_pzero(func_decl *f, expr_ref & result); void mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result); @@ -142,8 +142,7 @@ public: obj_map const & const2bv() const { return m_const2bv; } obj_map const & rm_const2bv() const { return m_rm_const2bv; } - obj_map const & uf2bvuf() const { return m_uf2bvuf; } - obj_map const & uf23bvuf() const { return m_uf23bvuf; } + obj_map const & uf2bvuf() const { return m_uf2bvuf; } void reset(void); @@ -151,6 +150,8 @@ public: expr_ref_vector m_extra_assertions; protected: + void mk_one(func_decl *f, expr_ref sign, expr_ref & result); + void mk_is_nan(expr * e, expr_ref & result); void mk_is_inf(expr * e, expr_ref & result); void mk_is_pinf(expr * e, expr_ref & result); @@ -183,7 +184,11 @@ protected: expr_ref & c_sgn, expr_ref & c_sig, expr_ref & c_exp, expr_ref & d_sgn, expr_ref & d_sig, expr_ref & d_exp, expr_ref & res_sgn, expr_ref & res_sig, expr_ref & res_exp); - app * mk_fresh_const(char const * prefix, unsigned sz); + app * mk_fresh_const(char const * prefix, unsigned sz); + + void mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result); + + void mk_uninterpreted_output(sort * rng, func_decl * fbv, expr_ref_buffer & new_args, expr_ref & result); }; #endif diff --git a/src/ast/fpa/fpa2bv_rewriter.h b/src/ast/fpa/fpa2bv_rewriter.h index ed885a4cc..7d0f4d06b 100644 --- a/src/ast/fpa/fpa2bv_rewriter.h +++ b/src/ast/fpa/fpa2bv_rewriter.h @@ -17,8 +17,8 @@ Notes: --*/ -#ifndef _FPA2BV_REWRITER_H_ -#define _FPA2BV_REWRITER_H_ +#ifndef FPA2BV_REWRITER_H_ +#define FPA2BV_REWRITER_H_ #include"cooperate.h" #include"rewriter_def.h" @@ -91,7 +91,9 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { if (m().is_eq(f)) { SASSERT(num == 2); - SASSERT(m().get_sort(args[0]) == m().get_sort(args[1])); + TRACE("fpa2bv_rw", tout << "(= " << mk_ismt2_pp(args[0], m()) << " " << + mk_ismt2_pp(args[1], m()) << ")" << std::endl;); + SASSERT(m().get_sort(args[0]) == m().get_sort(args[1])); sort * ds = f->get_domain()[0]; if (m_conv.is_float(ds)) { m_conv.mk_eq(args[0], args[1], result); @@ -103,8 +105,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { } return BR_FAILED; } - - if (m().is_ite(f)) { + else if (m().is_ite(f)) { SASSERT(num == 3); if (m_conv.is_float(args[1])) { m_conv.mk_ite(args[0], args[1], args[2], result); @@ -112,6 +113,14 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { } return BR_FAILED; } + else if (m().is_distinct(f)) { + sort * ds = f->get_domain()[0]; + if (m_conv.is_float(ds) || m_conv.is_rm(ds)) { + m_conv.mk_distinct(f, num, args, result); + return BR_DONE; + } + return BR_FAILED; + } if (m_conv.is_float_family(f)) { switch (f->get_decl_kind()) { @@ -169,15 +178,12 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { } } - if (f->get_family_id() == null_family_id) + if (f->get_family_id() != 0 && f->get_family_id() != m_conv.fu().get_family_id()) { - bool is_float_uf = m_conv.is_float(f->get_range()); - unsigned i = 0; - while (!is_float_uf && i < num) - { - is_float_uf = m_conv.is_float(f->get_domain()[i]); - i++; - } + bool is_float_uf = m_conv.is_float(f->get_range()) || m_conv.is_rm(f->get_range()); + + for (unsigned i = 0; i < num; i++) + is_float_uf |= m_conv.is_float(f->get_domain()[i]) || m_conv.is_rm(f->get_domain()[i]); if (is_float_uf) { diff --git a/src/ast/fpa_decl_plugin.cpp b/src/ast/fpa_decl_plugin.cpp index e559ee179..b7d7dc015 100644 --- a/src/ast/fpa_decl_plugin.cpp +++ b/src/ast/fpa_decl_plugin.cpp @@ -289,7 +289,7 @@ func_decl * fpa_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_param func_decl * fpa_decl_plugin::mk_bin_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { - if (arity != 2) + if (arity < 2) m_manager->raise_exception("invalid number of arguments to floating point relation"); if (domain[0] != domain[1] || !is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected equal FloatingPoint sorts as arguments"); @@ -306,7 +306,7 @@ func_decl * fpa_decl_plugin::mk_bin_rel_decl(decl_kind k, unsigned num_parameter } func_decl_info finfo(m_family_id, k); finfo.set_chainable(true); - return m_manager->mk_func_decl(name, arity, domain, m_manager->mk_bool_sort(), finfo); + return m_manager->mk_func_decl(name, domain[0], domain[1], m_manager->mk_bool_sort(), finfo); } func_decl * fpa_decl_plugin::mk_unary_rel_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -629,7 +629,6 @@ func_decl * fpa_decl_plugin::mk_to_sbv(decl_kind k, unsigned num_parameters, par symbol name("fp.to_sbv"); sort * bvs = m_bv_plugin->mk_sort(BV_SORT, 1, parameters); return m_manager->mk_func_decl(name, arity, domain, bvs, func_decl_info(m_family_id, k, num_parameters, parameters)); - } func_decl * fpa_decl_plugin::mk_to_real(decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -643,10 +642,10 @@ func_decl * fpa_decl_plugin::mk_to_real(decl_kind k, unsigned num_parameters, pa return m_manager->mk_func_decl(name, 1, domain, m_real_sort, func_decl_info(m_family_id, k)); } -func_decl * fpa_decl_plugin::mk_float_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * fpa_decl_plugin::mk_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 1) - m_manager->raise_exception("invalid number of arguments to asIEEEBV"); + m_manager->raise_exception("invalid number of arguments to to_ieee_bv"); if (!is_float_sort(domain[0])) m_manager->raise_exception("sort mismatch, expected argument of FloatingPoint sort"); @@ -792,7 +791,7 @@ func_decl * fpa_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, case OP_FPA_TO_FP_UNSIGNED: return mk_to_fp_unsigned(k, num_parameters, parameters, arity, domain, range); case OP_FPA_TO_IEEE_BV: - return mk_float_to_ieee_bv(k, num_parameters, parameters, arity, domain, range); + return mk_to_ieee_bv(k, num_parameters, parameters, arity, domain, range); case OP_FPA_INTERNAL_BVWRAP: return mk_internal_bv_wrap(k, num_parameters, parameters, arity, domain, range); case OP_FPA_INTERNAL_BVUNWRAP: @@ -1014,7 +1013,13 @@ app * fpa_util::mk_internal_to_sbv_unspecified(unsigned width) { return m().mk_app(get_family_id(), OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED, 1, ps, 0, 0, range); } +app * fpa_util::mk_internal_to_ieee_bv_unspecified(unsigned width) { + parameter ps[] = { parameter(width) }; + sort * range = m_bv_util.mk_sort(width); + return m().mk_app(get_family_id(), OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED, 1, ps, 0, 0, range); +} + app * fpa_util::mk_internal_to_real_unspecified() { sort * range = m_a_util.mk_real(); return m().mk_app(get_family_id(), OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED, 0, 0, 0, 0, range); -} \ No newline at end of file +} diff --git a/src/ast/fpa_decl_plugin.h b/src/ast/fpa_decl_plugin.h index 0e7901aca..5ee68797e 100644 --- a/src/ast/fpa_decl_plugin.h +++ b/src/ast/fpa_decl_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _fpa_decl_plugin_H_ -#define _fpa_decl_plugin_H_ +#ifndef fpa_decl_plugin_H_ +#define fpa_decl_plugin_H_ #include"ast.h" #include"id_gen.h" @@ -89,6 +89,7 @@ enum fpa_op_kind { OP_FPA_INTERNAL_BVUNWRAP, OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED, OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED, + OP_FPA_INTERNAL_TO_IEEE_BV_UNSPECIFIED, OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED, LAST_FLOAT_OP @@ -152,8 +153,8 @@ class fpa_decl_plugin : public decl_plugin { unsigned arity, sort * const * domain, sort * range); func_decl * mk_to_real(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); - func_decl * mk_float_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_to_ieee_bv(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); func_decl * mk_internal_bv_wrap(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); @@ -329,10 +330,11 @@ public: bool is_neg(expr * a) { return is_app_of(a, m_fid, OP_FPA_NEG); } - app * mk_float_to_ieee_bv(expr * arg1) { return m().mk_app(m_fid, OP_FPA_TO_IEEE_BV, arg1); } + app * mk_to_ieee_bv(expr * arg1) { return m().mk_app(m_fid, OP_FPA_TO_IEEE_BV, arg1); } app * mk_internal_to_ubv_unspecified(unsigned width); app * mk_internal_to_sbv_unspecified(unsigned width); + app * mk_internal_to_ieee_bv_unspecified(unsigned width); app * mk_internal_to_real_unspecified(); bool is_wrap(expr * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_BVWRAP); } diff --git a/src/ast/func_decl_dependencies.cpp b/src/ast/func_decl_dependencies.cpp index 162efb0dd..d53c2d9b1 100644 --- a/src/ast/func_decl_dependencies.cpp +++ b/src/ast/func_decl_dependencies.cpp @@ -145,24 +145,24 @@ class func_decl_dependencies::top_sort { return false; m_todo.push_back(f); while (!m_todo.empty()) { - func_decl * f = m_todo.back(); + func_decl * cf = m_todo.back(); - switch (get_color(f)) { + switch (get_color(cf)) { case CLOSED: m_todo.pop_back(); break; case OPEN: - set_color(f, IN_PROGRESS); - if (visit_children(f)) { - SASSERT(m_todo.back() == f); + set_color(cf, IN_PROGRESS); + if (visit_children(cf)) { + SASSERT(m_todo.back() == cf); m_todo.pop_back(); - set_color(f, CLOSED); + set_color(cf, CLOSED); } break; case IN_PROGRESS: - if (all_children_closed(f)) { - SASSERT(m_todo.back() == f); - set_color(f, CLOSED); + if (all_children_closed(cf)) { + SASSERT(m_todo.back() == cf); + set_color(cf, CLOSED); } else { m_todo.reset(); return true; diff --git a/src/ast/func_decl_dependencies.h b/src/ast/func_decl_dependencies.h index f88062063..e354540c2 100644 --- a/src/ast/func_decl_dependencies.h +++ b/src/ast/func_decl_dependencies.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _FUNC_DECL_DEPENDENCIES_H_ -#define _FUNC_DECL_DEPENDENCIES_H_ +#ifndef FUNC_DECL_DEPENDENCIES_H_ +#define FUNC_DECL_DEPENDENCIES_H_ #include"ast.h" #include"obj_hashtable.h" diff --git a/src/ast/has_free_vars.h b/src/ast/has_free_vars.h index d3d856ca1..cb7a6273a 100644 --- a/src/ast/has_free_vars.h +++ b/src/ast/has_free_vars.h @@ -16,12 +16,12 @@ Author: Revision History: --*/ -#ifndef _HAS_FREE_VARS_H_ -#define _HAS_FREE_VARS_H_ +#ifndef HAS_FREE_VARS_H_ +#define HAS_FREE_VARS_H_ class expr; bool has_free_vars(expr * n); -#endif /* _HAS_FREE_VARS_H_ */ +#endif /* HAS_FREE_VARS_H_ */ diff --git a/src/ast/macro_substitution.h b/src/ast/macro_substitution.h index d3c9b3c9e..356449590 100644 --- a/src/ast/macro_substitution.h +++ b/src/ast/macro_substitution.h @@ -18,8 +18,8 @@ Author: Notes: --*/ -#ifndef _MACRO_SUBSTITUTION_H_ -#define _MACRO_SUBSTITUTION_H_ +#ifndef MACRO_SUBSTITUTION_H_ +#define MACRO_SUBSTITUTION_H_ #include"ast.h" diff --git a/src/ast/macros/macro_finder.h b/src/ast/macros/macro_finder.h index bfe1d4ecb..541ec80b7 100644 --- a/src/ast/macros/macro_finder.h +++ b/src/ast/macros/macro_finder.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MACRO_FINDER_H_ -#define _MACRO_FINDER_H_ +#ifndef MACRO_FINDER_H_ +#define MACRO_FINDER_H_ #include"macro_manager.h" #include"arith_simplifier_plugin.h" @@ -51,5 +51,5 @@ public: void operator()(unsigned n, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); }; -#endif /* _MACRO_FINDER_H_ */ +#endif /* MACRO_FINDER_H_ */ diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp index abcc97882..75b4e55f4 100644 --- a/src/ast/macros/macro_manager.cpp +++ b/src/ast/macros/macro_manager.cpp @@ -255,9 +255,9 @@ bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref app * n = to_app(_n); quantifier * q = 0; func_decl * d = n->get_decl(); - TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m_manager) << "\nd:\n" << d->get_name() << "\n";); + TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";); if (m_macro_manager.m_decl2macro.find(d, q)) { - TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m_manager) << "\n";); + TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";); app * head = 0; expr * def = 0; m_macro_manager.get_head_def(q, d, head, def); @@ -272,17 +272,17 @@ bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref SASSERT(subst_args[nidx] == 0); subst_args[nidx] = n->get_arg(i); } - var_subst s(m_manager); + var_subst s(m); s(def, num, subst_args.c_ptr(), r); - if (m_manager.proofs_enabled()) { - expr_ref instance(m_manager); + if (m.proofs_enabled()) { + expr_ref instance(m); s(q->get_expr(), num, subst_args.c_ptr(), instance); - proof * qi_pr = m_manager.mk_quant_inst(m_manager.mk_or(m_manager.mk_not(q), instance), num, subst_args.c_ptr()); + proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr()); proof * q_pr = 0; m_macro_manager.m_decl2macro_pr.find(d, q_pr); SASSERT(q_pr != 0); proof * prs[2] = { qi_pr, q_pr }; - p = m_manager.mk_unit_resolution(2, prs); + p = m.mk_unit_resolution(2, prs); } else { p = 0; diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h index 480f37d9c..bc015de41 100644 --- a/src/ast/macros/macro_manager.h +++ b/src/ast/macros/macro_manager.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MACRO_MANAGER_H_ -#define _MACRO_MANAGER_H_ +#ifndef MACRO_MANAGER_H_ +#define MACRO_MANAGER_H_ #include"ast_util.h" #include"obj_hashtable.h" @@ -95,5 +95,5 @@ public: }; -#endif /* _MACRO_MANAGER_H_ */ +#endif /* MACRO_MANAGER_H_ */ diff --git a/src/ast/macros/macro_util.h b/src/ast/macros/macro_util.h index 9bc32af69..2a1581162 100644 --- a/src/ast/macros/macro_util.h +++ b/src/ast/macros/macro_util.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _MACRO_UTIL_H_ -#define _MACRO_UTIL_H_ +#ifndef MACRO_UTIL_H_ +#define MACRO_UTIL_H_ #include"ast.h" #include"obj_hashtable.h" diff --git a/src/ast/macros/quasi_macros.h b/src/ast/macros/quasi_macros.h index 64e72e348..5640fad30 100644 --- a/src/ast/macros/quasi_macros.h +++ b/src/ast/macros/quasi_macros.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _QUASI_MACROS_H_ -#define _QUASI_MACROS_H_ +#ifndef QUASI_MACROS_H_ +#define QUASI_MACROS_H_ #include #include"macro_manager.h" diff --git a/src/ast/normal_forms/defined_names.h b/src/ast/normal_forms/defined_names.h index 0b9f04569..69e365f3d 100644 --- a/src/ast/normal_forms/defined_names.h +++ b/src/ast/normal_forms/defined_names.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _DEFINED_NAMES_H_ -#define _DEFINED_NAMES_H_ +#ifndef DEFINED_NAMES_H_ +#define DEFINED_NAMES_H_ #include"ast.h" @@ -85,5 +85,5 @@ public: func_decl * get_name_decl(unsigned i) const; }; -#endif /* _DEFINED_NAMES_H_ */ +#endif /* DEFINED_NAMES_H_ */ diff --git a/src/ast/normal_forms/name_exprs.h b/src/ast/normal_forms/name_exprs.h index a725297ec..9d351375a 100644 --- a/src/ast/normal_forms/name_exprs.h +++ b/src/ast/normal_forms/name_exprs.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _NAME_EXPRS_H_ -#define _NAME_EXPRS_H_ +#ifndef NAME_EXPRS_H_ +#define NAME_EXPRS_H_ #include"ast.h" #include"defined_names.h" diff --git a/src/ast/normal_forms/nnf.h b/src/ast/normal_forms/nnf.h index e8296e933..122b85974 100644 --- a/src/ast/normal_forms/nnf.h +++ b/src/ast/normal_forms/nnf.h @@ -17,8 +17,8 @@ Notes: Major revision on 2011-10-06 --*/ -#ifndef _NNF_H_ -#define _NNF_H_ +#ifndef NNF_H_ +#define NNF_H_ #include"ast.h" #include"params.h" @@ -52,4 +52,4 @@ public: void reset_cache(); }; -#endif /* _NNF_H_ */ +#endif /* NNF_H_ */ diff --git a/src/ast/normal_forms/pull_quant.h b/src/ast/normal_forms/pull_quant.h index 071fb0dbe..dcdae056b 100644 --- a/src/ast/normal_forms/pull_quant.h +++ b/src/ast/normal_forms/pull_quant.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _PULL_QUANT_H_ -#define _PULL_QUANT_H_ +#ifndef PULL_QUANT_H_ +#define PULL_QUANT_H_ #include"ast.h" @@ -56,4 +56,4 @@ public: void reset(); }; -#endif /* _PULL_QUANT_H_ */ +#endif /* PULL_QUANT_H_ */ diff --git a/src/ast/num_occurs.h b/src/ast/num_occurs.h index ecd77e356..3c51dbe5c 100644 --- a/src/ast/num_occurs.h +++ b/src/ast/num_occurs.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _NUM_OCCURS_H_ -#define _NUM_OCCURS_H_ +#ifndef NUM_OCCURS_H_ +#define NUM_OCCURS_H_ #include"ast.h" #include"obj_hashtable.h" @@ -51,5 +51,5 @@ public: } }; -#endif /* _NUM_OCCURS_H_ */ +#endif /* NUM_OCCURS_H_ */ diff --git a/src/ast/occurs.h b/src/ast/occurs.h index 1aa6b47a4..ffaeac766 100644 --- a/src/ast/occurs.h +++ b/src/ast/occurs.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _OCCURS_H_ -#define _OCCURS_H_ +#ifndef OCCURS_H_ +#define OCCURS_H_ class expr; class func_decl; @@ -32,5 +32,5 @@ bool occurs(expr * n1, expr * n2); */ bool occurs(func_decl * d, expr * n); -#endif /* _OCCURS_H_ */ +#endif /* OCCURS_H_ */ diff --git a/src/ast/pattern/expr_pattern_match.h b/src/ast/pattern/expr_pattern_match.h index 555d6a67e..c3c0256fd 100644 --- a/src/ast/pattern/expr_pattern_match.h +++ b/src/ast/pattern/expr_pattern_match.h @@ -17,8 +17,8 @@ Author: Notes: --*/ -#ifndef _EXPR_PATTERN_MATCH_H_ -#define _EXPR_PATTERN_MATCH_H_ +#ifndef EXPR_PATTERN_MATCH_H_ +#define EXPR_PATTERN_MATCH_H_ #include"ast.h" #include"map.h" diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index 83362f5b3..b93dd7657 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -116,7 +116,7 @@ void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) { n = e.m_node; unsigned delta = e.m_delta; TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";); - TRACE("collect_info", tout << mk_pp(n, m_manager) << "\n";); + TRACE("collect_info", tout << mk_pp(n, m) << "\n";); if (visit_children(n, delta)) { m_todo.pop_back(); save_candidate(n, delta); @@ -170,9 +170,9 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) { free_vars.insert(idx); info * i = 0; if (delta == 0) - i = alloc(info, m_manager, n, free_vars, 1); + i = alloc(info, m, n, free_vars, 1); else - i = alloc(info, m_manager, m_manager.mk_var(idx, to_var(n)->get_sort()), free_vars, 1); + i = alloc(info, m, m.mk_var(idx, to_var(n)->get_sort()), free_vars, 1); save(n, delta, i); } else { @@ -189,7 +189,7 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) { } if (c->get_num_args() == 0) { - save(n, delta, alloc(info, m_manager, n, uint_set(), 1)); + save(n, delta, alloc(info, m, n, uint_set(), 1)); return; } @@ -219,10 +219,10 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) { app * new_node = 0; if (changed) - new_node = m_manager.mk_app(decl, buffer.size(), buffer.c_ptr()); + new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr()); else new_node = to_app(n); - save(n, delta, alloc(info, m_manager, new_node, free_vars, size)); + save(n, delta, alloc(info, m, new_node, free_vars, size)); // Remark: arithmetic patterns are only used if they are nested inside other terms. // That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern // if arithmetic is not in the forbidden list. @@ -235,7 +235,7 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) { decl_kind k = c->get_decl_kind(); if (!free_vars.empty() && (fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) { - TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m_manager) << "\n";); + TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";); m_owner.add_candidate(new_node, free_vars, size); } return; @@ -338,7 +338,7 @@ bool pattern_inference::contains_subpattern::operator()(expr * n) { uint_set const & s2 = e->get_data().m_value.m_free_vars; SASSERT(s2.subset_of(s1)); if (s1 == s2) { - TRACE("pattern_inference", tout << mk_pp(n, m_owner.m_manager) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m_manager) << "\n";); + TRACE("pattern_inference", tout << mk_pp(n, m_owner.m) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m) << "\n";); return true; } } @@ -411,7 +411,7 @@ void pattern_inference::candidates2unary_patterns(ptr_vector const & candid expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { - app * new_pattern = m_manager.mk_pattern(candidate); + app * new_pattern = m.mk_pattern(candidate); result.push_back(new_pattern); } else { @@ -435,7 +435,7 @@ void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns, for (unsigned j = 0; j < m_pre_patterns.size(); j++) { pre_pattern * curr = m_pre_patterns[j]; if (curr->m_free_vars.num_elems() == m_num_bindings) { - app * new_pattern = m_manager.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr()); + app * new_pattern = m.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr()); result.push_back(new_pattern); if (result.size() >= max_num_patterns) return; @@ -489,7 +489,7 @@ bool pattern_inference::is_forbidden(app * n) const { // occur outside of the quantifier. That is, Z3 will never match this kind of // pattern. if (m_params.m_pi_avoid_skolems && decl->is_skolem()) { - CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m_manager) << "\n";); + CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m) << "\n";); return true; } if (is_forbidden(decl)) @@ -509,8 +509,8 @@ bool pattern_inference::has_preferred_patterns(ptr_vector & candidate_patte expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { - TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m_manager) << "\n";); - app * p = m_manager.mk_pattern(candidate); + TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m) << "\n";); + app * p = m.mk_pattern(candidate); result.push_back(p); found = true; } @@ -531,11 +531,11 @@ void pattern_inference::mk_patterns(unsigned num_bindings, m_collect(n, num_bindings); TRACE("pattern_inference", - tout << mk_pp(n, m_manager); + tout << mk_pp(n, m); tout << "\ncandidates:\n"; unsigned num = m_candidates.size(); for (unsigned i = 0; i < num; i++) { - tout << mk_pp(m_candidates.get(i), m_manager) << "\n"; + tout << mk_pp(m_candidates.get(i), m) << "\n"; }); if (!m_candidates.empty()) { @@ -543,7 +543,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings, filter_looping_patterns(m_tmp1); TRACE("pattern_inference", tout << "candidates after removing looping-patterns:\n"; - dump_app_vector(tout, m_tmp1, m_manager);); + dump_app_vector(tout, m_tmp1, m);); SASSERT(!m_tmp1.empty()); if (!has_preferred_patterns(m_tmp1, result)) { // continue if there are no preferred patterns @@ -552,7 +552,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings, SASSERT(!m_tmp2.empty()); TRACE("pattern_inference", tout << "candidates after removing bigger patterns:\n"; - dump_app_vector(tout, m_tmp2, m_manager);); + dump_app_vector(tout, m_tmp2, m);); m_tmp1.reset(); candidates2unary_patterns(m_tmp2, m_tmp1, result); unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns; @@ -563,7 +563,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings, std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt); TRACE("pattern_inference", tout << "candidates after sorting:\n"; - dump_app_vector(tout, m_tmp1, m_manager);); + dump_app_vector(tout, m_tmp1, m);); candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result); } } @@ -577,7 +577,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings, #include"database.h" // defines g_pattern_database void pattern_inference::reduce1_quantifier(quantifier * q) { - TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m_manager) << "\n";); + TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";); if (!q->is_forall()) { simplifier::reduce1_quantifier(q); return; @@ -587,27 +587,27 @@ void pattern_inference::reduce1_quantifier(quantifier * q) { if (m_params.m_pi_use_database) { m_database.initialize(g_pattern_database); - app_ref_vector new_patterns(m_manager); + app_ref_vector new_patterns(m); unsigned new_weight; if (m_database.match_quantifier(q, new_patterns, new_weight)) { #ifdef Z3DEBUG - for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m_manager, new_patterns.get(i))); } + for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m, new_patterns.get(i))); } #endif - quantifier_ref new_q(m_manager); + quantifier_ref new_q(m); if (q->get_num_patterns() > 0) { // just update the weight... - TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m_manager) << "\n";); - new_q = m_manager.update_quantifier_weight(q, new_weight); + TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m) << "\n";); + new_q = m.update_quantifier_weight(q, new_weight); } else { - quantifier_ref tmp(m_manager); - tmp = m_manager.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr()); - new_q = m_manager.update_quantifier_weight(tmp, new_weight); - TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m_manager) << "\n";); + quantifier_ref tmp(m); + tmp = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr()); + new_q = m.update_quantifier_weight(tmp, new_weight); + TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m) << "\n";); } proof * pr = 0; - if (m_manager.fine_grain_proofs()) - pr = m_manager.mk_rewrite(q, new_q); + if (m.fine_grain_proofs()) + pr = m.mk_rewrite(q, new_q); cache_result(q, new_q, pr); return; } @@ -635,7 +635,7 @@ void pattern_inference::reduce1_quantifier(quantifier * q) { new_no_patterns.push_back(new_pattern); } - app_ref_buffer new_patterns(m_manager); + app_ref_buffer new_patterns(m); if (m_params.m_pi_arith == AP_CONSERVATIVE) m_forbidden.push_back(m_afid); @@ -677,26 +677,26 @@ void pattern_inference::reduce1_quantifier(quantifier * q) { warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=).", q->get_qid().str().c_str(), weight); } - // verbose_stream() << mk_pp(q, m_manager) << "\n"; + // verbose_stream() << mk_pp(q, m) << "\n"; } } } - quantifier_ref new_q(m_manager); - new_q = m_manager.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body); + quantifier_ref new_q(m); + new_q = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body); if (weight != q->get_weight()) - new_q = m_manager.update_quantifier_weight(new_q, weight); - proof_ref pr(m_manager); - if (m_manager.fine_grain_proofs()) { + new_q = m.update_quantifier_weight(new_q, weight); + proof_ref pr(m); + if (m.fine_grain_proofs()) { if (new_body_pr == 0) - new_body_pr = m_manager.mk_reflexivity(new_body); - pr = m_manager.mk_quant_intro(q, new_q, new_body_pr); + new_body_pr = m.mk_reflexivity(new_body); + pr = m.mk_quant_intro(q, new_q, new_body_pr); } if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) { - pull_quant pull(m_manager); - expr_ref new_expr(m_manager); - proof_ref new_pr(m_manager); + pull_quant pull(m); + expr_ref new_expr(m); + proof_ref new_pr(m); pull(new_q, new_expr, new_pr); quantifier * new_new_q = to_quantifier(new_expr); if (new_new_q != new_q) { @@ -705,12 +705,12 @@ void pattern_inference::reduce1_quantifier(quantifier * q) { if (m_params.m_pi_warnings) { warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str()); } - new_q = m_manager.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr()); - if (m_manager.fine_grain_proofs()) { - pr = m_manager.mk_transitivity(pr, new_pr); - pr = m_manager.mk_transitivity(pr, m_manager.mk_quant_intro(new_new_q, new_q, m_manager.mk_reflexivity(new_q->get_expr()))); + new_q = m.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr()); + if (m.fine_grain_proofs()) { + pr = m.mk_transitivity(pr, new_pr); + pr = m.mk_transitivity(pr, m.mk_quant_intro(new_new_q, new_q, m.mk_reflexivity(new_q->get_expr()))); } - TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m_manager) << "\n";); + TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";); } } } @@ -719,7 +719,7 @@ void pattern_inference::reduce1_quantifier(quantifier * q) { if (m_params.m_pi_warnings) { warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str()); } - TRACE("pi_failed", tout << mk_pp(q, m_manager) << "\n";); + TRACE("pi_failed", tout << mk_pp(q, m) << "\n";); } if (new_patterns.empty() && new_body == q->get_expr()) { diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index 97897835c..d4ab708e9 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _PATTERN_INFERENCE_H_ -#define _PATTERN_INFERENCE_H_ +#ifndef PATTERN_INFERENCE_H_ +#define PATTERN_INFERENCE_H_ #include"ast.h" #include"simplifier.h" @@ -38,7 +38,7 @@ Revision History: every instance of f(g(X)) is also an instance of f(X). */ class smaller_pattern { - ast_manager & m_manager; + ast_manager & m; ptr_vector m_bindings; typedef std::pair expr_pair; @@ -54,7 +54,7 @@ class smaller_pattern { public: smaller_pattern(ast_manager & m): - m_manager(m) { + m(m) { } bool operator()(unsigned num_bindings, expr * p1, expr * p2); @@ -135,7 +135,7 @@ class pattern_inference : public simplifier { m_node(n, m), m_free_vars(vars), m_size(sz) {} }; - ast_manager & m_manager; + ast_manager & m; pattern_inference & m_owner; family_id m_afid; unsigned m_num_bindings; @@ -150,7 +150,7 @@ class pattern_inference : public simplifier { void save_candidate(expr * n, unsigned delta); void reset(); public: - collect(ast_manager & m, pattern_inference & o):m_manager(m), m_owner(o), m_afid(m.mk_family_id("arith")) {} + collect(ast_manager & m, pattern_inference & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {} void operator()(expr * n, unsigned num_bindings); }; @@ -244,5 +244,5 @@ public: bool is_forbidden(app * n) const; }; -#endif /* _PATTERN_INFERENCE_H_ */ +#endif /* PATTERN_INFERENCE_H_ */ diff --git a/src/ast/pattern/pattern_inference_params.h b/src/ast/pattern/pattern_inference_params.h index dc9f0cb9b..a941b7dd6 100644 --- a/src/ast/pattern/pattern_inference_params.h +++ b/src/ast/pattern/pattern_inference_params.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _PATTERN_INFERENCE_PARAMS_H_ -#define _PATTERN_INFERENCE_PARAMS_H_ +#ifndef PATTERN_INFERENCE_PARAMS_H_ +#define PATTERN_INFERENCE_PARAMS_H_ #include"params.h" @@ -48,5 +48,5 @@ struct pattern_inference_params { void updt_params(params_ref const & _p); }; -#endif /* _PATTERN_INFERENCE_PARAMS_H_ */ +#endif /* PATTERN_INFERENCE_PARAMS_H_ */ diff --git a/src/ast/pb_decl_plugin.cpp b/src/ast/pb_decl_plugin.cpp new file mode 100644 index 000000000..f4e3fdf97 --- /dev/null +++ b/src/ast/pb_decl_plugin.cpp @@ -0,0 +1,288 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pb_decl_plugin.cpp + +Abstract: + + Cardinality Constraints plugin + +Author: + + Nikolaj Bjorner (nbjorner) 2013-05-11 + +Revision History: + +--*/ + +#include "pb_decl_plugin.h" + +pb_decl_plugin::pb_decl_plugin(): + m_at_most_sym("at-most"), + m_at_least_sym("at-least"), + m_pble_sym("pble"), + m_pbge_sym("pbge"), + m_pbeq_sym("pbeq") +{} + +func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) { + SASSERT(m_manager); + ast_manager& m = *m_manager; + for (unsigned i = 0; i < arity; ++i) { + if (!m.is_bool(domain[i])) { + m.raise_exception("invalid non-Boolean sort applied to 'at-most'"); + } + } + symbol sym; + switch(k) { + case OP_AT_LEAST_K: sym = m_at_least_sym; break; + case OP_AT_MOST_K: sym = m_at_most_sym; break; + case OP_PB_LE: sym = m_pble_sym; break; + case OP_PB_GE: sym = m_pbge_sym; break; + case OP_PB_EQ: sym = m_pbeq_sym; break; + default: break; + } + switch(k) { + case OP_AT_LEAST_K: + case OP_AT_MOST_K: { + if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() < 0) { + m.raise_exception("function expects one non-negative integer parameter"); + } + func_decl_info info(m_family_id, k, 1, parameters); + return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); + } + case OP_PB_GE: + case OP_PB_LE: + case OP_PB_EQ: { + if (num_parameters != 1 + arity) { + m.raise_exception("function expects arity+1 rational parameters"); + } + vector params; + for (unsigned i = 0; i < num_parameters; ++i) { + parameter const& p = parameters[i]; + if (p.is_int()) { + params.push_back(p); + } + else if (p.is_rational()) { + // HACK: ast pretty printer does not work with rationals. + rational r = p.get_rational(); + if (r.is_int32()) { + params.push_back(parameter(r.get_int32())); + } + else { + params.push_back(p); + } + } + else { + m.raise_exception("functions 'pble/pbge/pbeq' expect arity+1 integer parameters"); + } + } + func_decl_info info(m_family_id, k, num_parameters, params.c_ptr()); + return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); + } + default: + UNREACHABLE(); + return 0; + } +} + +void pb_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { + if (logic == symbol::null) { + op_names.push_back(builtin_name(m_at_most_sym.bare_str(), OP_AT_MOST_K)); + op_names.push_back(builtin_name(m_at_least_sym.bare_str(), OP_AT_LEAST_K)); + op_names.push_back(builtin_name(m_pble_sym.bare_str(), OP_PB_LE)); + op_names.push_back(builtin_name(m_pbge_sym.bare_str(), OP_PB_GE)); + op_names.push_back(builtin_name(m_pbeq_sym.bare_str(), OP_PB_EQ)); + } +} + +app * pb_util::mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { + vector params; + params.push_back(parameter(k)); + for (unsigned i = 0; i < num_args; ++i) { + params.push_back(parameter(coeffs[i])); + } + return m.mk_app(m_fid, OP_PB_LE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort()); +} + +app * pb_util::mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { + vector params; + params.push_back(parameter(k)); + for (unsigned i = 0; i < num_args; ++i) { + params.push_back(parameter(coeffs[i])); + } + return m.mk_app(m_fid, OP_PB_GE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort()); +} + +app * pb_util::mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { + vector params; + params.push_back(parameter(k)); + for (unsigned i = 0; i < num_args; ++i) { + params.push_back(parameter(coeffs[i])); + } + return m.mk_app(m_fid, OP_PB_EQ, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort()); +} + +// ax + by < k +// <=> +// -ax - by >= -k + 1 +// <=> +// a(1-x) + b(1-y) >= -k + a + b + 1 +app * pb_util::mk_lt(unsigned num_args, rational const * _coeffs, expr * const * _args, rational const& _k) { + vector coeffs; + rational k(_k); + expr_ref_vector args(m); + expr* f; + rational d(denominator(k)); + for (unsigned i = 0; i < num_args; ++i) { + coeffs.push_back(_coeffs[i]); + d = lcm(d, denominator(coeffs[i])); + if (m.is_not(_args[i], f)) { + args.push_back(f); + } + else { + args.push_back(m.mk_not(_args[i])); + } + } + if (!d.is_one()) { + k *= d; + for (unsigned i = 0; i < num_args; ++i) { + coeffs[i] *= d; + } + } + k.neg(); + k += rational::one(); + for (unsigned i = 0; i < num_args; ++i) { + k += coeffs[i]; + } + return mk_ge(num_args, coeffs.c_ptr(), args.c_ptr(), k); +} + + +app * pb_util::mk_at_most_k(unsigned num_args, expr * const * args, unsigned k) { + parameter param(k); + return m.mk_app(m_fid, OP_AT_MOST_K, 1, ¶m, num_args, args, m.mk_bool_sort()); +} + +bool pb_util::is_at_most_k(func_decl *a) const { + return is_decl_of(a, m_fid, OP_AT_MOST_K); +} + +bool pb_util::is_at_most_k(expr *a, rational& k) const { + if (is_at_most_k(a)) { + k = get_k(a); + return true; + } + else { + return false; + } +} + +app * pb_util::mk_at_least_k(unsigned num_args, expr * const * args, unsigned k) { + parameter param(k); + return m.mk_app(m_fid, OP_AT_LEAST_K, 1, ¶m, num_args, args, m.mk_bool_sort()); +} + +bool pb_util::is_at_least_k(func_decl *a) const { + return is_decl_of(a, m_fid, OP_AT_LEAST_K); +} + +bool pb_util::is_at_least_k(expr *a, rational& k) const { + if (is_at_least_k(a)) { + k = get_k(a); + return true; + } + else { + return false; + } +} + +rational pb_util::get_k(func_decl *a) const { + parameter const& p = a->get_parameter(0); + if (is_at_most_k(a) || is_at_least_k(a)) { + return to_rational(p); + } + else { + SASSERT(is_le(a) || is_ge(a) || is_eq(a)); + return to_rational(p); + } +} + + +bool pb_util::is_le(func_decl *a) const { + return is_decl_of(a, m_fid, OP_PB_LE); +} + +bool pb_util::is_le(expr* a, rational& k) const { + if (is_le(a)) { + k = get_k(a); + return true; + } + else { + return false; + } +} + +bool pb_util::is_ge(func_decl *a) const { + return is_decl_of(a, m_fid, OP_PB_GE); +} + +bool pb_util::is_ge(expr* a, rational& k) const { + if (is_ge(a)) { + k = get_k(a); + return true; + } + else { + return false; + } +} + + +bool pb_util::is_eq(func_decl *a) const { + return is_decl_of(a, m_fid, OP_PB_EQ); +} + +bool pb_util::is_eq(expr* a, rational& k) const { + if (is_eq(a)) { + k = get_k(a); + return true; + } + else { + return false; + } +} + +rational pb_util::get_coeff(func_decl* a, unsigned index) const { + if (is_at_most_k(a) || is_at_least_k(a)) { + return rational::one(); + } + SASSERT(is_le(a) || is_ge(a) || is_eq(a)); + SASSERT(1 + index < a->get_num_parameters()); + return to_rational(a->get_parameter(index + 1)); +} + +rational pb_util::to_rational(parameter const& p) const { + if (p.is_int()) { + return rational(p.get_int()); + } + SASSERT(p.is_rational()); + return p.get_rational(); +} + +bool pb_util::has_unit_coefficients(func_decl* f) const { + if (is_at_most_k(f) || is_at_least_k(f)) return true; + unsigned sz = f->get_arity(); + for (unsigned i = 0; i < sz; ++i) { + if (!get_coeff(f, i).is_one()) return false; + } + return true; +} + +app* pb_util::mk_fresh_bool() { + symbol name = m.mk_fresh_var_name("pb"); + func_decl_info info(m_fid, OP_PB_AUX_BOOL, 0, 0); + return m.mk_const(m.mk_func_decl(name, 0, (sort *const*)0, m.mk_bool_sort(), info)); +} diff --git a/src/ast/pb_decl_plugin.h b/src/ast/pb_decl_plugin.h new file mode 100644 index 000000000..d4264b9b9 --- /dev/null +++ b/src/ast/pb_decl_plugin.h @@ -0,0 +1,128 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pb_decl_plugin.h + +Abstract: + + Pseudo-Boolean and Cardinality Constraints plugin + +Author: + + Nikolaj Bjorner (nbjorner) 2013-05-11 + +Notes: + + + (at-most-k x1 .... x_n) means x1 + ... + x_n <= k + +hence: + + (not (at-most-k x1 .... x_n)) means x1 + ... + x_n >= k + 1 + + +--*/ +#ifndef PB_DECL_PLUGIN_H_ +#define PB_DECL_PLUGIN_H_ + +#include"ast.h" + +enum pb_op_kind { + OP_AT_MOST_K, // at most K Booleans are true. + OP_AT_LEAST_K, // at least K Booleans are true. + OP_PB_LE, // pseudo-Boolean <= (generalizes at_most_k) + OP_PB_GE, // pseudo-Boolean >= + OP_PB_EQ, // equality + OP_PB_AUX_BOOL, // auxiliary internal Boolean variable. + LAST_PB_OP +}; + + +class pb_decl_plugin : public decl_plugin { + symbol m_at_most_sym; + symbol m_at_least_sym; + symbol m_pble_sym; + symbol m_pbge_sym; + symbol m_pbeq_sym; + func_decl * mk_at_most(unsigned arity, unsigned k); + func_decl * mk_at_least(unsigned arity, unsigned k); + func_decl * mk_le(unsigned arity, rational const* coeffs, int k); + func_decl * mk_ge(unsigned arity, rational const* coeffs, int k); + func_decl * mk_eq(unsigned arity, rational const* coeffs, int k); +public: + pb_decl_plugin(); + virtual ~pb_decl_plugin() {} + + virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { + UNREACHABLE(); + return 0; + } + + virtual decl_plugin * mk_fresh() { + return alloc(pb_decl_plugin); + } + + // + // Contract for func_decl: + // parameters[0] - integer (at most k elements) + // all sorts are Booleans + // parameters[1] .. parameters[arity] - coefficients + virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); + virtual void get_op_names(svector & op_names, symbol const & logic); + +}; + + +class pb_util { + ast_manager & m; + family_id m_fid; +public: + pb_util(ast_manager& m):m(m), m_fid(m.mk_family_id("pb")) {} + ast_manager & get_manager() const { return m; } + family_id get_family_id() const { return m_fid; } + app * mk_at_most_k(unsigned num_args, expr * const * args, unsigned k); + app * mk_at_least_k(unsigned num_args, expr * const * args, unsigned k); + app * mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); + app * mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); + app * mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); + app * mk_lt(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k); + bool is_at_most_k(func_decl *a) const; + bool is_at_most_k(expr *a) const { return is_app(a) && is_at_most_k(to_app(a)->get_decl()); } + bool is_at_most_k(expr *a, rational& k) const; + bool is_at_least_k(func_decl *a) const; + bool is_at_least_k(expr *a) const { return is_app(a) && is_at_least_k(to_app(a)->get_decl()); } + bool is_at_least_k(expr *a, rational& k) const; + rational get_k(func_decl *a) const; + rational get_k(expr *a) const { return get_k(to_app(a)->get_decl()); } + bool is_le(func_decl *a) const; + bool is_le(expr *a) const { return is_app(a) && is_le(to_app(a)->get_decl()); } + bool is_le(expr* a, rational& k) const; + bool is_ge(func_decl* a) const; + bool is_ge(expr* a) const { return is_app(a) && is_ge(to_app(a)->get_decl()); } + bool is_ge(expr* a, rational& k) const; + bool is_aux_bool(expr* e) const { return is_app_of(e, m_fid, OP_PB_AUX_BOOL); } + rational get_coeff(expr* a, unsigned index) const { return get_coeff(to_app(a)->get_decl(), index); } + rational get_coeff(func_decl* a, unsigned index) const; + bool has_unit_coefficients(func_decl* f) const; + bool has_unit_coefficients(expr* f) const { return is_app(f) && has_unit_coefficients(to_app(f)->get_decl()); } + + + bool is_eq(func_decl* f) const; + bool is_eq(expr* e) const { return is_app(e) && is_eq(to_app(e)->get_decl()); } + bool is_eq(expr* e, rational& k) const; + + app* mk_fresh_bool(); + + +private: + rational to_rational(parameter const& p) const; +}; + + + + +#endif /* PB_DECL_PLUGIN_H_ */ + diff --git a/src/ast/pp.h b/src/ast/pp.h index f567afa4a..bfb907552 100644 --- a/src/ast/pp.h +++ b/src/ast/pp.h @@ -16,13 +16,13 @@ Author: Revision History: --*/ -#ifndef _PP_H_ -#define _PP_H_ +#ifndef PP_H_ +#define PP_H_ #include"format.h" #include"params.h" void pp(std::ostream & out, format_ns::format * f, ast_manager & m, params_ref const & p = params_ref()); -#endif /* _PP_H_ */ +#endif /* PP_H_ */ diff --git a/src/ast/pp_params.pyg b/src/ast/pp_params.pyg index 7424b516f..d831cada9 100644 --- a/src/ast/pp_params.pyg +++ b/src/ast/pp_params.pyg @@ -10,7 +10,7 @@ def_module_params('pp', ('decimal', BOOL, False, 'pretty print real numbers using decimal notation (the output may be truncated). Z3 adds a ? if the value is not precise'), ('decimal_precision', UINT, 10, 'maximum number of decimal places to be used when pp.decimal=true'), ('bv_literals', BOOL, True, 'use Bit-Vector literals (e.g, #x0F and #b0101) during pretty printing'), - ('fp_real_literals', BOOL, False, 'use real-numbered floating point literals (e.g, +1.0p-1) during pretty printing'), + ('fp_real_literals', BOOL, False, 'use real-numbered floating point literals (e.g, +1.0p-1) during pretty printing'), ('bv_neg', BOOL, False, 'use bvneg when displaying Bit-Vector literals where the most significant bit is 1'), ('flat_assoc', BOOL, True, 'flat associative operators (when pretty printing SMT2 terms/formulas)'), ('fixed_indent', BOOL, False, 'use a fixed indentation for applications'), diff --git a/src/ast/proof_checker/proof_checker.cpp b/src/ast/proof_checker/proof_checker.cpp index 41c43b26c..16546db1e 100644 --- a/src/ast/proof_checker/proof_checker.cpp +++ b/src/ast/proof_checker/proof_checker.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "proof_checker.h" #include "ast_ll_pp.h" #include "ast_pp.h" diff --git a/src/ast/proof_checker/proof_checker.h b/src/ast/proof_checker/proof_checker.h index 9c5f8a749..5e1a170ee 100644 --- a/src/ast/proof_checker/proof_checker.h +++ b/src/ast/proof_checker/proof_checker.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _PROOF_CHECKER_H_ -#define _PROOF_CHECKER_H_ +#ifndef PROOF_CHECKER_H_ +#define PROOF_CHECKER_H_ #include "ast.h" #include "map.h" diff --git a/src/ast/recurse_expr.h b/src/ast/recurse_expr.h index 6b3220d40..46375d955 100644 --- a/src/ast/recurse_expr.h +++ b/src/ast/recurse_expr.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _RECURSE_EXPR_H_ -#define _RECURSE_EXPR_H_ +#ifndef RECURSE_EXPR_H_ +#define RECURSE_EXPR_H_ #include"ast.h" #include"obj_hashtable.h" @@ -44,4 +44,4 @@ public: void finalize() { m_cache.finalize(); m_todo.finalize(); } }; -#endif /* _RECURSE_EXPR_H_ */ +#endif /* RECURSE_EXPR_H_ */ diff --git a/src/ast/recurse_expr_def.h b/src/ast/recurse_expr_def.h index 7638668a2..5149baa93 100644 --- a/src/ast/recurse_expr_def.h +++ b/src/ast/recurse_expr_def.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _RECURSE_EXPR_DEF_H_ -#define _RECURSE_EXPR_DEF_H_ +#ifndef RECURSE_EXPR_DEF_H_ +#define RECURSE_EXPR_DEF_H_ #include"recurse_expr.h" @@ -106,4 +106,4 @@ T recurse_expr::operator()(expr * r return get_cached(r); } -#endif /* _RECURSE_EXPR_DEF_H_ */ +#endif /* RECURSE_EXPR_DEF_H_ */ diff --git a/src/ast/reg_decl_plugins.cpp b/src/ast/reg_decl_plugins.cpp index f46dd76d4..b4ff63ede 100644 --- a/src/ast/reg_decl_plugins.cpp +++ b/src/ast/reg_decl_plugins.cpp @@ -24,6 +24,7 @@ Revision History: #include"datatype_decl_plugin.h" #include"dl_decl_plugin.h" #include"seq_decl_plugin.h" +#include"pb_decl_plugin.h" #include"fpa_decl_plugin.h" void reg_decl_plugins(ast_manager & m) { @@ -48,4 +49,7 @@ void reg_decl_plugins(ast_manager & m) { if (!m.get_plugin(m.mk_family_id(symbol("fpa")))) { m.register_plugin(symbol("fpa"), alloc(fpa_decl_plugin)); } + if (!m.get_plugin(m.mk_family_id(symbol("pb")))) { + m.register_plugin(symbol("pb"), alloc(pb_decl_plugin)); + } } diff --git a/src/ast/reg_decl_plugins.h b/src/ast/reg_decl_plugins.h index 57185181e..5684780be 100644 --- a/src/ast/reg_decl_plugins.h +++ b/src/ast/reg_decl_plugins.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _REG_DECL_PLUGINS_H_ -#define _REG_DECL_PLUGINS_H_ +#ifndef REG_DECL_PLUGINS_H_ +#define REG_DECL_PLUGINS_H_ class ast_manager; diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index d08e6f13a..6c8501ccb 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _ARITH_REWRITER_H_ -#define _ARITH_REWRITER_H_ +#ifndef ARITH_REWRITER_H_ +#define ARITH_REWRITER_H_ #include"poly_rewriter.h" #include"arith_decl_plugin.h" diff --git a/src/ast/rewriter/array_rewriter.h b/src/ast/rewriter/array_rewriter.h index 5f20f61ba..10b7bcfda 100644 --- a/src/ast/rewriter/array_rewriter.h +++ b/src/ast/rewriter/array_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _ARRAY_REWRITER_H_ -#define _ARRAY_REWRITER_H_ +#ifndef ARRAY_REWRITER_H_ +#define ARRAY_REWRITER_H_ #include"array_decl_plugin.h" #include"rewriter_types.h" diff --git a/src/ast/rewriter/ast_counter.cpp b/src/ast/rewriter/ast_counter.cpp index 6f49a232f..f2c8cabf7 100644 --- a/src/ast/rewriter/ast_counter.cpp +++ b/src/ast/rewriter/ast_counter.cpp @@ -18,12 +18,9 @@ Revision History: --*/ #include "ast_counter.h" -#include "var_subst.h" void counter::update(unsigned el, int delta) { int & counter = get(el); - SASSERT(!m_stay_non_negative || counter>=0); - SASSERT(!m_stay_non_negative || static_cast(counter)>=-delta); counter += delta; } @@ -89,19 +86,17 @@ int counter::get_max_counter_value() const { return res; } -void var_counter::count_vars(ast_manager & m, const app * pred, int coef) { +void var_counter::count_vars(const app * pred, int coef) { unsigned n = pred->get_num_args(); for (unsigned i = 0; i < n; i++) { - m_sorts.reset(); - m_todo.reset(); - m_mark.reset(); - ::get_free_vars(m_mark, m_todo, pred->get_arg(i), m_sorts); - for (unsigned j = 0; j < m_sorts.size(); ++j) { - if (m_sorts[j]) { + m_fv(pred->get_arg(i)); + for (unsigned j = 0; j < m_fv.size(); ++j) { + if (m_fv[j]) { update(j, coef); } } } + m_fv.reset(); } diff --git a/src/ast/rewriter/ast_counter.h b/src/ast/rewriter/ast_counter.h index e7251079f..4509d2549 100644 --- a/src/ast/rewriter/ast_counter.h +++ b/src/ast/rewriter/ast_counter.h @@ -21,22 +21,22 @@ Revision History: --*/ -#ifndef _AST_COUNTER_H_ -#define _AST_COUNTER_H_ +#ifndef AST_COUNTER_H_ +#define AST_COUNTER_H_ #include "ast.h" #include "map.h" #include "uint_set.h" +#include "var_subst.h" class counter { protected: typedef u_map map_impl; map_impl m_data; - const bool m_stay_non_negative; public: typedef map_impl::iterator iterator; - counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} + counter() {} void reset() { m_data.reset(); } iterator begin() const { return m_data.begin(); } @@ -69,15 +69,14 @@ public: class var_counter : public counter { protected: - ptr_vector m_sorts; expr_fast_mark1 m_visited; + expr_free_vars m_fv; ptr_vector m_todo; - ast_mark m_mark; unsigned_vector m_scopes; unsigned get_max_var(bool & has_var); public: - var_counter(bool stay_non_negative = true): counter(stay_non_negative) {} - void count_vars(ast_manager & m, const app * t, int coef = 1); + var_counter() {} + void count_vars(const app * t, int coef = 1); unsigned get_max_var(expr* e); unsigned get_next_var(expr* e); }; @@ -85,11 +84,10 @@ public: class ast_counter { typedef obj_map map_impl; map_impl m_data; - bool m_stay_non_negative; public: typedef map_impl::iterator iterator; - ast_counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} + ast_counter() {} iterator begin() const { return m_data.begin(); } iterator end() const { return m_data.end(); } @@ -99,7 +97,6 @@ class ast_counter { } void update(ast * el, int delta){ get(el) += delta; - SASSERT(!m_stay_non_negative || get(el) >= 0); } void inc(ast * el) { update(el, 1); } diff --git a/src/ast/rewriter/bit_blaster/bit_blaster.h b/src/ast/rewriter/bit_blaster/bit_blaster.h index 7b317c6cb..958c79b4d 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _BIT_BLASTER_H_ -#define _BIT_BLASTER_H_ +#ifndef BIT_BLASTER_H_ +#define BIT_BLASTER_H_ #include"basic_simplifier_plugin.h" #include"bit_blaster_params.h" @@ -61,5 +61,5 @@ public: bit_blaster_params const & get_params() const { return this->m_params; } }; -#endif /* _BIT_BLASTER_H_ */ +#endif /* BIT_BLASTER_H_ */ diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_params.h b/src/ast/rewriter/bit_blaster/bit_blaster_params.h index 653c8fc74..ee32d005a 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_params.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_params.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _BIT_BLASTER_PARAMS_H_ -#define _BIT_BLASTER_PARAMS_H_ +#ifndef BIT_BLASTER_PARAMS_H_ +#define BIT_BLASTER_PARAMS_H_ struct bit_blaster_params { bool m_bb_ext_gates; @@ -34,5 +34,5 @@ struct bit_blaster_params { #endif }; -#endif /* _BIT_BLASTER_PARAMS_H_ */ +#endif /* BIT_BLASTER_PARAMS_H_ */ diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index 63843d31e..0d78a8dc4 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -92,6 +92,9 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { expr_ref_vector m_out; obj_map m_const2bits; expr_ref_vector m_bindings; + func_decl_ref_vector m_keys; + expr_ref_vector m_values; + unsigned_vector m_keyval_lim; bool m_blast_mul; bool m_blast_add; @@ -116,12 +119,13 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { m_in1(m), m_in2(m), m_out(m), - m_bindings(m) { + m_bindings(m), + m_keys(m), + m_values(m) { updt_params(p); } ~blaster_rewriter_cfg() { - dec_ref_map_key_values(m_manager, m_const2bits); } void updt_params(params_ref const & p) { @@ -157,6 +161,24 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { } } + void push() { + m_keyval_lim.push_back(m_keys.size()); + } + + void pop(unsigned num_scopes) { + if (num_scopes > 0) { + unsigned new_sz = m_keyval_lim.size() - num_scopes; + unsigned lim = m_keyval_lim[new_sz]; + for (unsigned i = m_keys.size(); i > lim; ) { + --i; + m_const2bits.remove(m_keys[i].get()); + } + m_keys.resize(lim); + m_values.resize(lim); + m_keyval_lim.resize(new_sz); + } + } + template app * mk_mkbv(V const & bits) { return m().mk_app(butil().get_family_id(), OP_MKBV, bits.size(), bits.c_ptr()); @@ -180,8 +202,8 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { } r = mk_mkbv(m_out); m_const2bits.insert(f, r); - m_manager.inc_ref(f); - m_manager.inc_ref(r); + m_keys.push_back(f); + m_values.push_back(r); result = r; } @@ -611,6 +633,8 @@ struct bit_blaster_rewriter::imp : public rewriter_tpl { m_cfg(m, m_blaster, p) { SASSERT(m_blaster.butil().get_family_id() == m.get_family_id("bv")); } + void push() { m_cfg.push(); } + void pop(unsigned s) { m_cfg.pop(s); } }; bit_blaster_rewriter::bit_blaster_rewriter(ast_manager & m, params_ref const & p): @@ -630,6 +654,14 @@ void bit_blaster_rewriter::set_cancel(bool f) { m_imp->m_blaster.set_cancel(f); } +void bit_blaster_rewriter::push() { + m_imp->push(); +} + +void bit_blaster_rewriter::pop(unsigned num_scopes) { + m_imp->pop(num_scopes); +} + ast_manager & bit_blaster_rewriter::m() const { return m_imp->m(); } diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h index 4a0f3e2fe..3b4715657 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _BIT_BLASTER_REWRITER_H_ -#define _BIT_BLASTER_REWRITER_H_ +#ifndef BIT_BLASTER_REWRITER_H_ +#define BIT_BLASTER_REWRITER_H_ #include"ast.h" #include"obj_hashtable.h" @@ -36,6 +36,8 @@ public: void cleanup(); obj_map const& const2bits() const; void operator()(expr * e, expr_ref & result, proof_ref & result_proof); + void push(); + void pop(unsigned num_scopes); }; #endif diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h index ca60d0f5c..b66cb7026 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _BIT_BLASTER_TPL_H_ -#define _BIT_BLASTER_TPL_H_ +#ifndef BIT_BLASTER_TPL_H_ +#define BIT_BLASTER_TPL_H_ #include"rational.h" diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index a30c0d32c..9284ff420 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -464,18 +464,18 @@ void bit_blaster_tpl::mk_udiv_urem(unsigned sz, expr * const * a_bits, expr // update p if (i < sz - 1) { for (unsigned j = sz - 1; j > 0; j--) { - expr_ref i(m()); - mk_ite(q, t.get(j-1), p.get(j-1), i); - p.set(j, i); + expr_ref ie(m()); + mk_ite(q, t.get(j-1), p.get(j-1), ie); + p.set(j, ie); } p.set(0, a_bits[sz - i - 2]); } else { // last step: p contains the remainder for (unsigned j = 0; j < sz; j++) { - expr_ref i(m()); - mk_ite(q, t.get(j), p.get(j), i); - p.set(j, i); + expr_ref ie(m()); + mk_ite(q, t.get(j), p.get(j), ie); + p.set(j, ie); } } } @@ -1041,6 +1041,11 @@ void bit_blaster_tpl::mk_ext_rotate_left_right(unsigned sz, expr * const * mk_rotate_right(sz, a_bits, static_cast(k.get_uint64()), out_bits); } else { + // + // Review: a better tuned implementation is possible by using shifts by power of two. + // e.g., looping over the bits of b_bits, then rotate by a power of two depending + // on the bit-position. This would get rid of the mk_urem. + // expr_ref_vector sz_bits(m()); expr_ref_vector masked_b_bits(m()); expr_ref_vector eqs(m()); diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index ef6dc2d38..b309f8032 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _BOOL_REWRITER_H_ -#define _BOOL_REWRITER_H_ +#ifndef BOOL_REWRITER_H_ +#define BOOL_REWRITER_H_ #include"ast.h" #include"rewriter.h" diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 6d8c21666..1c9b44b52 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _BV_REWRITER_H_ -#define _BV_REWRITER_H_ +#ifndef BV_REWRITER_H_ +#define BV_REWRITER_H_ #include"poly_rewriter.h" #include"bv_decl_plugin.h" @@ -168,6 +168,8 @@ public: } br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result); + + bool hi_div0() const { return m_hi_div0; } }; #endif diff --git a/src/ast/rewriter/datatype_rewriter.cpp b/src/ast/rewriter/datatype_rewriter.cpp index 8c55ba498..be198c3d9 100644 --- a/src/ast/rewriter/datatype_rewriter.cpp +++ b/src/ast/rewriter/datatype_rewriter.cpp @@ -60,6 +60,32 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr UNREACHABLE(); break; } + case OP_DT_UPDATE_FIELD: { + SASSERT(num_args == 2); + if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0]))) + return BR_FAILED; + app * a = to_app(args[0]); + func_decl * c_decl = a->get_decl(); + if (c_decl != m_util.get_accessor_constructor(f)) { + result = a; + return BR_DONE; + } + ptr_vector const * acc = m_util.get_constructor_accessors(c_decl); + SASSERT(acc && acc->size() == a->get_num_args()); + unsigned num = acc->size(); + ptr_buffer new_args; + for (unsigned i = 0; i < num; ++i) { + + if (f == (*acc)[i]) { + new_args.push_back(args[1]); + } + else { + new_args.push_back(a->get_arg(i)); + } + } + result = m().mk_app(c_decl, num, new_args.c_ptr()); + return BR_DONE; + } default: UNREACHABLE(); } diff --git a/src/ast/rewriter/datatype_rewriter.h b/src/ast/rewriter/datatype_rewriter.h index 46663a6d8..43fbc46d9 100644 --- a/src/ast/rewriter/datatype_rewriter.h +++ b/src/ast/rewriter/datatype_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _DATATYPE_REWRITER_H_ -#define _DATATYPE_REWRITER_H_ +#ifndef DATATYPE_REWRITER_H_ +#define DATATYPE_REWRITER_H_ #include"datatype_decl_plugin.h" #include"rewriter_types.h" diff --git a/src/ast/rewriter/der.h b/src/ast/rewriter/der.h index a0be33c5c..07ff581dd 100644 --- a/src/ast/rewriter/der.h +++ b/src/ast/rewriter/der.h @@ -18,8 +18,8 @@ Revision History: Christoph Wintersteiger, 2010-03-30: Added Destr. Multi-Equality Resolution --*/ -#ifndef _DER_H_ -#define _DER_H_ +#ifndef DER_H_ +#define DER_H_ #include"ast.h" #include"var_subst.h" @@ -183,5 +183,5 @@ public: typedef der_rewriter der_star; -#endif /* _DER_H_ */ +#endif /* DER_H_ */ diff --git a/src/ast/rewriter/dl_rewriter.h b/src/ast/rewriter/dl_rewriter.h index 1ef5c577a..ecb3f0944 100644 --- a/src/ast/rewriter/dl_rewriter.h +++ b/src/ast/rewriter/dl_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _DL_REWRITER_H_ -#define _DL_REWRITER_H_ +#ifndef DL_REWRITER_H_ +#define DL_REWRITER_H_ #include"dl_decl_plugin.h" #include"rewriter_types.h" diff --git a/src/ast/rewriter/expr_replacer.h b/src/ast/rewriter/expr_replacer.h index 5bd72dc76..a770abe55 100644 --- a/src/ast/rewriter/expr_replacer.h +++ b/src/ast/rewriter/expr_replacer.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _EXPR_REPLACER_H_ -#define _EXPR_REPLACER_H_ +#ifndef EXPR_REPLACER_H_ +#define EXPR_REPLACER_H_ #include"ast.h" #include"expr_substitution.h" diff --git a/src/ast/rewriter/expr_safe_replace.cpp b/src/ast/rewriter/expr_safe_replace.cpp index a4886ad1a..b43577960 100644 --- a/src/ast/rewriter/expr_safe_replace.cpp +++ b/src/ast/rewriter/expr_safe_replace.cpp @@ -29,43 +29,45 @@ void expr_safe_replace::insert(expr* src, expr* dst) { } void expr_safe_replace::operator()(expr* e, expr_ref& res) { - obj_map cache; - ptr_vector todo, args; - expr_ref_vector refs(m); - todo.push_back(e); + m_todo.push_back(e); expr* a, *b, *d; - todo.push_back(e); - while (!todo.empty()) { - a = todo.back(); - if (cache.contains(a)) { - todo.pop_back(); + while (!m_todo.empty()) { + a = m_todo.back(); + if (m_cache.contains(a)) { + m_todo.pop_back(); } else if (m_subst.find(a, b)) { - cache.insert(a, b); - todo.pop_back(); + m_cache.insert(a, b); + m_todo.pop_back(); } else if (is_var(a)) { - cache.insert(a, a); - todo.pop_back(); + m_cache.insert(a, a); + m_todo.pop_back(); } else if (is_app(a)) { app* c = to_app(a); unsigned n = c->get_num_args(); - args.reset(); + m_args.reset(); + bool arg_differs = false; for (unsigned i = 0; i < n; ++i) { - if (cache.find(c->get_arg(i), d)) { - args.push_back(d); + if (m_cache.find(c->get_arg(i), d)) { + m_args.push_back(d); + arg_differs |= c->get_arg(i) != d; } else { - todo.push_back(c->get_arg(i)); + m_todo.push_back(c->get_arg(i)); } } - if (args.size() == n) { - b = m.mk_app(c->get_decl(), args.size(), args.c_ptr()); - refs.push_back(b); - cache.insert(a, b); - todo.pop_back(); + if (m_args.size() == n) { + if (arg_differs) { + b = m.mk_app(c->get_decl(), m_args.size(), m_args.c_ptr()); + m_refs.push_back(b); + } else { + b = a; + } + m_cache.insert(a, b); + m_todo.pop_back(); } } else { @@ -93,12 +95,16 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) { } replace(q->get_expr(), new_body); b = m.update_quantifier(q, pats.size(), pats.c_ptr(), nopats.size(), nopats.c_ptr(), new_body); - refs.push_back(b); - cache.insert(a, b); - todo.pop_back(); + m_refs.push_back(b); + m_cache.insert(a, b); + m_todo.pop_back(); } } - res = cache.find(e); + res = m_cache.find(e); + m_cache.reset(); + m_todo.reset(); + m_args.reset(); + m_refs.reset(); } void expr_safe_replace::reset() { diff --git a/src/ast/rewriter/expr_safe_replace.h b/src/ast/rewriter/expr_safe_replace.h index ad626d18f..fe7033439 100644 --- a/src/ast/rewriter/expr_safe_replace.h +++ b/src/ast/rewriter/expr_safe_replace.h @@ -19,8 +19,8 @@ Revision History: --*/ -#ifndef __EXPR_SAFE_REPLACE_H__ -#define __EXPR_SAFE_REPLACE_H__ +#ifndef EXPR_SAFE_REPLACE_H_ +#define EXPR_SAFE_REPLACE_H_ #include "ast.h" @@ -29,9 +29,12 @@ class expr_safe_replace { expr_ref_vector m_src; expr_ref_vector m_dst; obj_map m_subst; + obj_map m_cache; + ptr_vector m_todo, m_args; + expr_ref_vector m_refs; public: - expr_safe_replace(ast_manager& m): m(m), m_src(m), m_dst(m) {} + expr_safe_replace(ast_manager& m): m(m), m_src(m), m_dst(m), m_refs(m) {} void insert(expr* src, expr* dst); @@ -42,6 +45,8 @@ public: void apply_substitution(expr* s, expr* def, expr_ref& t); void reset(); + + bool empty() const { return m_subst.empty(); } }; -#endif /* __EXPR_SAFE_REPLACE_H__ */ +#endif /* EXPR_SAFE_REPLACE_H_ */ diff --git a/src/ast/rewriter/factor_rewriter.h b/src/ast/rewriter/factor_rewriter.h index ee6a1d03a..389eacd87 100644 --- a/src/ast/rewriter/factor_rewriter.h +++ b/src/ast/rewriter/factor_rewriter.h @@ -17,8 +17,8 @@ Author: Notes: --*/ -#ifndef _FACTOR_REWRITER_H_ -#define _FACTOR_REWRITER_H_ +#ifndef FACTOR_REWRITER_H_ +#define FACTOR_REWRITER_H_ #include"ast.h" #include"rewriter.h" diff --git a/src/ast/rewriter/fpa_rewriter.cpp b/src/ast/rewriter/fpa_rewriter.cpp index 6aee683ff..9701e448e 100644 --- a/src/ast/rewriter/fpa_rewriter.cpp +++ b/src/ast/rewriter/fpa_rewriter.cpp @@ -70,7 +70,7 @@ br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con case OP_FPA_MAX: SASSERT(num_args == 2); st = mk_max(args[0], args[1], result); break; case OP_FPA_FMA: SASSERT(num_args == 4); st = mk_fma(args[0], args[1], args[2], args[3], result); break; case OP_FPA_SQRT: SASSERT(num_args == 2); st = mk_sqrt(args[0], args[1], result); break; - case OP_FPA_ROUND_TO_INTEGRAL: SASSERT(num_args == 2); st = mk_round(args[0], args[1], result); break; + case OP_FPA_ROUND_TO_INTEGRAL: SASSERT(num_args == 2); st = mk_round_to_integral(args[0], args[1], result); break; case OP_FPA_EQ: SASSERT(num_args == 2); st = mk_float_eq(args[0], args[1], result); break; case OP_FPA_LT: SASSERT(num_args == 2); st = mk_lt(args[0], args[1], result); break; @@ -90,8 +90,8 @@ br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con case OP_FPA_TO_FP_UNSIGNED: SASSERT(num_args == 2); st = mk_to_fp_unsigned(f, args[0], args[1], result); break; case OP_FPA_TO_UBV: SASSERT(num_args == 2); st = mk_to_ubv(f, args[0], args[1], result); break; case OP_FPA_TO_SBV: SASSERT(num_args == 2); st = mk_to_sbv(f, args[0], args[1], result); break; - case OP_FPA_TO_REAL: SASSERT(num_args == 1); st = mk_to_real(args[0], result); break; - case OP_FPA_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(args[0], result); break; + case OP_FPA_TO_IEEE_BV: SASSERT(num_args == 1); st = mk_to_ieee_bv(f, args[0], result); break; + case OP_FPA_TO_REAL: SASSERT(num_args == 1); st = mk_to_real(args[0], result); break; case OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED: SASSERT(num_args == 0); st = mk_to_ubv_unspecified(f, result); break; @@ -121,7 +121,7 @@ br_status fpa_rewriter::mk_to_ubv_unspecified(func_decl * f, expr_ref & result) // The "hardware interpretation" is 0. result = bu.mk_numeral(0, bv_sz); else - result = m_util.mk_internal_to_real_unspecified(); + result = m_util.mk_internal_to_ubv_unspecified(bv_sz); return BR_DONE; } @@ -136,16 +136,31 @@ br_status fpa_rewriter::mk_to_sbv_unspecified(func_decl * f, expr_ref & result) // The "hardware interpretation" is 0. result = bu.mk_numeral(0, bv_sz); else - result = m_util.mk_internal_to_real_unspecified(); + result = m_util.mk_internal_to_sbv_unspecified(bv_sz); + + return BR_DONE; +} + +br_status fpa_rewriter::mk_to_ieee_bv_unspecified(func_decl * f, expr_ref & result) { + SASSERT(f->get_num_parameters() == 1); + SASSERT(f->get_parameter(0).is_int()); + unsigned bv_sz = f->get_parameter(0).get_int(); + + bv_util bu(m()); + if (m_hi_fp_unspecified) + // The "hardware interpretation" is 0. + result = bu.mk_numeral(0, bv_sz); + else + result = m_util.mk_internal_to_ieee_bv_unspecified(bv_sz); return BR_DONE; } br_status fpa_rewriter::mk_to_real_unspecified(expr_ref & result) { if (m_hi_fp_unspecified) - result = m_util.au().mk_numeral(0, false); - else // The "hardware interpretation" is 0. + result = m_util.au().mk_numeral(rational(0), false); + else result = m_util.mk_internal_to_real_unspecified(); return BR_DONE; @@ -243,7 +258,7 @@ br_status fpa_rewriter::mk_to_fp(func_decl * f, unsigned num_args, expr * const !m_util.au().is_numeral(args[2], r2)) return BR_FAILED; - TRACE("fp_rewriter", tout << "r1: " << r1 << ", r2: " << r2 << "\n";); + TRACE("fp_rewriter", tout << "r1: " << r1 << ", r2: " << r2 << "\n";); m_fm.set(v, ebits, sbits, rmv, r1.to_mpq(), r2.to_mpq().numerator()); result = m_util.mk_value(v); return BR_DONE; @@ -411,14 +426,24 @@ br_status fpa_rewriter::mk_min(expr * arg1, expr * arg2, expr_ref & result) { result = arg1; return BR_DONE; } - // expand as using ite's - result = m().mk_ite(m().mk_or(mk_eq_nan(arg1), m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2))), + if (m_util.is_zero(arg1) && m_util.is_zero(arg2)) { + result = arg2; + return BR_DONE; + } + + result = m().mk_ite(mk_eq_nan(arg1), arg2, - m().mk_ite(mk_eq_nan(arg2), - arg1, - m().mk_ite(m_util.mk_lt(arg1, arg2), - arg1, - arg2))); + m().mk_ite(mk_eq_nan(arg2), + arg1, + // min(-0.0, +0.0) = min(+0.0, -0.0) = +0.0 + m().mk_ite(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2), + m().mk_not(m().mk_eq(m_util.mk_is_positive(arg1), m_util.mk_is_positive(arg2)))), + m_util.mk_pzero(m().get_sort(arg1)), + m().mk_ite(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2)), + arg2, + m().mk_ite(m_util.mk_lt(arg1, arg2), + arg1, + arg2))))); return BR_REWRITE_FULL; } @@ -431,14 +456,24 @@ br_status fpa_rewriter::mk_max(expr * arg1, expr * arg2, expr_ref & result) { result = arg1; return BR_DONE; } - // expand as using ite's - result = m().mk_ite(m().mk_or(mk_eq_nan(arg1), m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2))), + if (m_util.is_zero(arg1) && m_util.is_zero(arg2)) { + result = arg2; + return BR_DONE; + } + + result = m().mk_ite(mk_eq_nan(arg1), arg2, - m().mk_ite(mk_eq_nan(arg2), - arg1, - m().mk_ite(m_util.mk_gt(arg1, arg2), - arg1, - arg2))); + m().mk_ite(mk_eq_nan(arg2), + arg1, + // max(-0.0, +0.0) = max(+0.0, -0.0) = +0.0 + m().mk_ite(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2), + m().mk_not(m().mk_eq(m_util.mk_is_positive(arg1), m_util.mk_is_positive(arg2)))), + m_util.mk_pzero(m().get_sort(arg1)), + m().mk_ite(m().mk_and(m_util.mk_is_zero(arg1), m_util.mk_is_zero(arg2)), + arg2, + m().mk_ite(m_util.mk_gt(arg1, arg2), + arg1, + arg2))))); return BR_REWRITE_FULL; } @@ -448,7 +483,7 @@ br_status fpa_rewriter::mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg scoped_mpf v2(m_fm), v3(m_fm), v4(m_fm); if (m_util.is_numeral(arg2, v2) && m_util.is_numeral(arg3, v3) && m_util.is_numeral(arg4, v4)) { scoped_mpf t(m_fm); - m_fm.fused_mul_add(rm, v2, v3, v4, t); + m_fm.fma(rm, v2, v3, v4, t); result = m_util.mk_value(t); return BR_DONE; } @@ -472,7 +507,7 @@ br_status fpa_rewriter::mk_sqrt(expr * arg1, expr * arg2, expr_ref & result) { return BR_FAILED; } -br_status fpa_rewriter::mk_round(expr * arg1, expr * arg2, expr_ref & result) { +br_status fpa_rewriter::mk_round_to_integral(expr * arg1, expr * arg2, expr_ref & result) { mpf_rounding_mode rm; if (m_util.is_rm_numeral(arg1, rm)) { scoped_mpf v2(m_fm); @@ -571,6 +606,7 @@ br_status fpa_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { br_status fpa_rewriter::mk_is_zero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); + if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_zero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; @@ -581,6 +617,7 @@ br_status fpa_rewriter::mk_is_zero(expr * arg1, expr_ref & result) { br_status fpa_rewriter::mk_is_nzero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); + if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_nzero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; @@ -591,6 +628,7 @@ br_status fpa_rewriter::mk_is_nzero(expr * arg1, expr_ref & result) { br_status fpa_rewriter::mk_is_pzero(expr * arg1, expr_ref & result) { scoped_mpf v(m_fm); + if (m_util.is_numeral(arg1, v)) { result = (m_fm.is_pzero(v)) ? m().mk_true() : m().mk_false(); return BR_DONE; @@ -675,10 +713,6 @@ br_status fpa_rewriter::mk_eq_core(expr * arg1, expr * arg2, expr_ref & result) return BR_FAILED; } -br_status fpa_rewriter::mk_to_ieee_bv(expr * arg1, expr_ref & result) { - return BR_FAILED; -} - br_status fpa_rewriter::mk_fp(expr * arg1, expr * arg2, expr * arg3, expr_ref & result) { unsynch_mpz_manager & mpzm = m_fm.mpz_manager(); bv_util bu(m()); @@ -716,7 +750,7 @@ br_status fpa_rewriter::mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ m_util.is_numeral(arg2, v)) { if (m_fm.is_nan(v) || m_fm.is_inf(v) || m_fm.is_neg(v)) { - result = m_util.mk_internal_to_ubv_unspecified(bv_sz); + mk_to_ubv_unspecified(f, result); return BR_REWRITE_FULL; } @@ -731,7 +765,7 @@ br_status fpa_rewriter::mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ if (r >= ll && r <= ul) result = bu.mk_numeral(r, bv_sz); else - result = m_util.mk_internal_to_ubv_unspecified(bv_sz); + mk_to_ubv_unspecified(f, result); return BR_DONE; } @@ -749,7 +783,7 @@ br_status fpa_rewriter::mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ m_util.is_numeral(arg2, v)) { if (m_fm.is_nan(v) || m_fm.is_inf(v)) { - result = m_util.mk_internal_to_sbv_unspecified(bv_sz); + mk_to_sbv_unspecified(f, result); return BR_REWRITE_FULL; } @@ -764,7 +798,27 @@ br_status fpa_rewriter::mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ if (r >= ll && r <= ul) result = bu.mk_numeral(r, bv_sz); else - result = m_util.mk_internal_to_sbv_unspecified(bv_sz); + mk_to_sbv_unspecified(f, result); + return BR_DONE; + } + + return BR_FAILED; +} + +br_status fpa_rewriter::mk_to_ieee_bv(func_decl * f, expr * arg, expr_ref & result) { + scoped_mpf v(m_fm); + + if (m_util.is_numeral(arg, v)) { + + if (m_fm.is_nan(v) || m_fm.is_inf(v)) { + mk_to_ieee_bv_unspecified(f, result); + return BR_REWRITE_FULL; + } + + bv_util bu(m()); + scoped_mpz rz(m_fm.mpq_manager()); + m_fm.to_ieee_bv_mpz(v, rz); + result = bu.mk_numeral(rational(rz), v.get().get_ebits() + v.get().get_sbits()); return BR_DONE; } diff --git a/src/ast/rewriter/fpa_rewriter.h b/src/ast/rewriter/fpa_rewriter.h index 2c76fad6a..41a929e4b 100644 --- a/src/ast/rewriter/fpa_rewriter.h +++ b/src/ast/rewriter/fpa_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _FLOAT_REWRITER_H_ -#define _FLOAT_REWRITER_H_ +#ifndef FLOAT_REWRITER_H_ +#define FLOAT_REWRITER_H_ #include"ast.h" #include"rewriter.h" @@ -57,7 +57,7 @@ public: br_status mk_max(expr * arg1, expr * arg2, expr_ref & result); br_status mk_fma(expr * arg1, expr * arg2, expr * arg3, expr * arg4, expr_ref & result); br_status mk_sqrt(expr * arg1, expr * arg2, expr_ref & result); - br_status mk_round(expr * arg1, expr * arg2, expr_ref & result); + br_status mk_round_to_integral(expr * arg1, expr * arg2, expr_ref & result); br_status mk_float_eq(expr * arg1, expr * arg2, expr_ref & result); br_status mk_lt(expr * arg1, expr * arg2, expr_ref & result); br_status mk_gt(expr * arg1, expr * arg2, expr_ref & result); @@ -81,10 +81,12 @@ public: br_status mk_to_fp_unsigned(expr * arg1, expr * arg2, expr_ref & result); br_status mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); br_status mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ref & result); + br_status mk_to_ieee_bv(func_decl * f, expr * arg, expr_ref & result); br_status mk_to_real(expr * arg, expr_ref & result); br_status mk_to_ubv_unspecified(func_decl * f, expr_ref & result); br_status mk_to_sbv_unspecified(func_decl * f, expr_ref & result); + br_status mk_to_ieee_bv_unspecified(func_decl * f, expr_ref & result); br_status mk_to_real_unspecified(expr_ref & result); }; diff --git a/src/ast/rewriter/mk_simplified_app.h b/src/ast/rewriter/mk_simplified_app.h index ca458cc16..40e6f993d 100644 --- a/src/ast/rewriter/mk_simplified_app.h +++ b/src/ast/rewriter/mk_simplified_app.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _MK_SIMPLIFIED_APP_H_ -#define _MK_SIMPLIFIED_APP_H_ +#ifndef MK_SIMPLIFIED_APP_H_ +#define MK_SIMPLIFIED_APP_H_ #include"ast.h" #include"params.h" diff --git a/src/ast/rewriter/pb_rewriter.cpp b/src/ast/rewriter/pb_rewriter.cpp new file mode 100644 index 000000000..eb85f8ec9 --- /dev/null +++ b/src/ast/rewriter/pb_rewriter.cpp @@ -0,0 +1,286 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pb_rewriter.cpp + +Abstract: + + Basic rewriting rules for PB constraints. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-14-12 + +Notes: + +--*/ + +#include "pb_rewriter.h" +#include "pb_rewriter_def.h" +#include "ast_pp.h" +#include "ast_util.h" +#include "ast_smt_pp.h" + + +class pb_ast_rewriter_util { + ast_manager& m; + expr_ref_vector m_refs; +public: + + typedef std::pair arg_t; + typedef vector args_t; + typedef rational numeral; + + pb_ast_rewriter_util(ast_manager& m): m(m), m_refs(m) {} + + expr* negate(expr* e) { + if (m.is_true(e)) { + return m.mk_false(); + } + if (m.is_false(e)) { + return m.mk_true(); + } + if (m.is_not(e, e)) { + return e; + } + m_refs.push_back(m.mk_not(e)); + return m_refs.back(); + } + + void display(std::ostream& out, expr* e) { + out << mk_pp(e, m); + } + + bool is_negated(expr* e) const { + return m.is_not(e); + } + + bool is_true(expr* e) const { + return m.is_true(e); + } + + bool is_false(expr* e) const { + return m.is_false(e); + } + + struct compare { + bool operator()(std::pair const& a, + std::pair const& b) { + return a.first->get_id() < b.first->get_id(); + } + + }; +}; + +expr_ref pb_rewriter::translate_pb2lia(obj_map& vars, expr* fml) { + pb_util util(m()); + arith_util a(m()); + expr_ref result(m()), tmp(m()); + expr_ref_vector es(m()); + expr*const* args = to_app(fml)->get_args(); + unsigned sz = to_app(fml)->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + expr* e = args[i]; + if (m().is_not(e, e)) { + es.push_back(a.mk_sub(a.mk_numeral(rational(1),true),vars.find(e))); + } + else { + es.push_back(vars.find(e)); + } + } + + if (util.is_at_most_k(fml) || util.is_at_least_k(fml)) { + if (es.empty()) { + tmp = a.mk_numeral(rational(0), true); + } + else { + tmp = a.mk_add(es.size(), es.c_ptr()); + } + if (util.is_at_most_k(fml)) { + result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false)); + } + else { + result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false)); + } + } + else if (util.is_le(fml) || util.is_ge(fml) || util.is_eq(fml)) { + for (unsigned i = 0; i < sz; ++i) { + es[i] = a.mk_mul(a.mk_numeral(util.get_coeff(fml, i),false), es[i].get()); + } + if (es.empty()) { + tmp = a.mk_numeral(rational(0), true); + } + else { + tmp = a.mk_add(es.size(), es.c_ptr()); + } + if (util.is_le(fml)) { + result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false)); + } + else if (util.is_ge(fml)) { + result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false)); + } + else { + result = m().mk_eq(tmp, a.mk_numeral(util.get_k(fml), false)); + } + } + else { + result = fml; + } + return result; +} + +expr_ref pb_rewriter::mk_validate_rewrite(app_ref& e1, app_ref& e2) { + ast_manager& m = e1.get_manager(); + arith_util a(m); + symbol name; + obj_map vars; + expr_ref_vector trail(m), fmls(m); + unsigned sz = to_app(e1)->get_num_args(); + expr*const*args = to_app(e1)->get_args(); + for (unsigned i = 0; i < sz; ++i) { + expr* e = args[i]; + if (m.is_true(e)) { + if (!vars.contains(e)) { + trail.push_back(a.mk_numeral(rational(1), true)); + vars.insert(e, trail.back()); + } + continue; + } + if (m.is_false(e)) { + if (!vars.contains(e)) { + trail.push_back(a.mk_numeral(rational(0), true)); + vars.insert(e, trail.back()); + } + continue; + } + + std::ostringstream strm; + strm << "x" << i; + name = symbol(strm.str().c_str()); + trail.push_back(m.mk_const(name, a.mk_int())); + expr* x = trail.back(); + m.is_not(e,e); + vars.insert(e, x); + fmls.push_back(a.mk_le(a.mk_numeral(rational(0), true), x)); + fmls.push_back(a.mk_le(x, a.mk_numeral(rational(1), true))); + } + expr_ref tmp(m); + expr_ref fml1 = translate_pb2lia(vars, e1); + expr_ref fml2 = translate_pb2lia(vars, e2); + tmp = m.mk_not(m.mk_eq(fml1, fml2)); + fmls.push_back(tmp); + tmp = m.mk_and(fmls.size(), fmls.c_ptr()); + return tmp; +} + +static unsigned s_lemma = 0; + +void pb_rewriter::validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml) { + ast_manager& m = fml.get_manager(); + app_ref tmp1(m), tmp2(m); + tmp1 = m.mk_app(f, sz, args); + tmp2 = to_app(fml); + expr_ref tmp = mk_validate_rewrite(tmp1, tmp2); + dump_pb_rewrite(tmp); +} + +void pb_rewriter::dump_pb_rewrite(expr* fml) { + std::ostringstream strm; + strm << "pb_rewrite_" << (s_lemma++) << ".smt2"; + std::ofstream out(strm.str().c_str()); + ast_smt_pp pp(m()); + pp.display_smt2(out, fml); + out.close(); +} + +br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { + ast_manager& m = result.get_manager(); + rational sum(0), maxsum(0); + for (unsigned i = 0; i < num_args; ++i) { + if (m.is_true(args[i])) { + sum += m_util.get_coeff(f, i); + maxsum += m_util.get_coeff(f, i); + } + else if (!m.is_false(args[i])) { + maxsum += m_util.get_coeff(f, i); + } + } + rational k = m_util.get_k(f); + + vector > vec; + for (unsigned i = 0; i < num_args; ++i) { + vec.push_back(std::make_pair(args[i], m_util.get_coeff(f, i))); + } + + switch(f->get_decl_kind()) { + case OP_AT_MOST_K: + case OP_PB_LE: + for (unsigned i = 0; i < num_args; ++i) { + vec[i].second.neg(); + } + k.neg(); + break; + case OP_AT_LEAST_K: + case OP_PB_GE: + case OP_PB_EQ: + break; + default: + UNREACHABLE(); + return BR_FAILED; + } + + bool is_eq = f->get_decl_kind() == OP_PB_EQ; + + pb_ast_rewriter_util pbu(m); + pb_rewriter_util util(pbu); + + util.unique(vec, k, is_eq); + lbool is_sat = util.normalize(vec, k, is_eq); + util.prune(vec, k, is_eq); + switch (is_sat) { + case l_true: + result = m.mk_true(); + break; + case l_false: + result = m.mk_false(); + break; + default: { + bool all_unit = true; + unsigned sz = vec.size(); + m_args.reset(); + m_coeffs.reset(); + for (unsigned i = 0; i < sz; ++i) { + m_args.push_back(vec[i].first); + m_coeffs.push_back(vec[i].second); + all_unit &= m_coeffs.back().is_one(); + } + if (is_eq) { + result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); + } + else if (all_unit && k.is_one()) { + result = mk_or(m, sz, m_args.c_ptr()); + } + else if (all_unit && k == rational(sz)) { + result = mk_and(m, sz, m_args.c_ptr()); + } + else { + result = m_util.mk_ge(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); + } + break; + } + } + TRACE("pb", + expr_ref tmp(m); + tmp = m.mk_app(f, num_args, args); + tout << tmp << "\n"; + tout << result << "\n"; + ); + TRACE("pb_validate", + validate_rewrite(f, num_args, args, result);); + + return BR_DONE; +} + + diff --git a/src/ast/rewriter/pb_rewriter.h b/src/ast/rewriter/pb_rewriter.h new file mode 100644 index 000000000..ba98d774e --- /dev/null +++ b/src/ast/rewriter/pb_rewriter.h @@ -0,0 +1,65 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pb_rewriter.h + +Abstract: + + Basic rewriting rules for PB constraints. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-14-12 + +Notes: + +--*/ +#ifndef PB_REWRITER_H_ +#define PB_REWRITER_H_ + +#include"pb_decl_plugin.h" +#include"rewriter_types.h" +#include"params.h" +#include"lbool.h" + + +template +class pb_rewriter_util { + PBU& m_util; + void display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); +public: + pb_rewriter_util(PBU& u) : m_util(u) {} + void unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); + lbool normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); + void prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq); +}; + +/** + \brief Cheap rewrite rules for PB constraints +*/ +class pb_rewriter { + pb_util m_util; + vector m_coeffs; + ptr_vector m_args; + + void validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml); +public: + pb_rewriter(ast_manager & m, params_ref const & p = params_ref()): + m_util(m) { + } + ast_manager & m() const { return m_util.get_manager(); } + family_id get_fid() const { return m_util.get_family_id(); } + + void updt_params(params_ref const & p) {} + static void get_param_descrs(param_descrs & r) {} + + br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); + + expr_ref translate_pb2lia(obj_map& vars, expr* fml); + expr_ref mk_validate_rewrite(app_ref& e1, app_ref& e2); + void dump_pb_rewrite(expr* fml); +}; + +#endif diff --git a/src/ast/rewriter/pb_rewriter_def.h b/src/ast/rewriter/pb_rewriter_def.h new file mode 100644 index 000000000..a713a05a7 --- /dev/null +++ b/src/ast/rewriter/pb_rewriter_def.h @@ -0,0 +1,298 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pb_rewriter_def.h + +Abstract: + + Basic rewriting rules for PB constraints. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-14-12 + +Notes: + +--*/ +#ifndef PB_REWRITER_DEF_H_ +#define PB_REWRITER_DEF_H_ + +#include"pb_rewriter.h" + + +template +void pb_rewriter_util::display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { + for (unsigned i = 0; i < args.size(); ++i) { + out << args[i].second << " * "; + m_util.display(out, args[i].first); + out << " "; + if (i+1 < args.size()) out << "+ "; + } + out << (is_eq?" = ":" >= ") << k << "\n"; +} + +template +void pb_rewriter_util::unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { + + TRACE("pb_verbose", display(tout << "pre-unique:", args, k, is_eq);); + for (unsigned i = 0; i < args.size(); ++i) { + if (m_util.is_negated(args[i].first)) { + args[i].first = m_util.negate(args[i].first); + k -= args[i].second; + args[i].second = -args[i].second; + } + } + // remove constants + for (unsigned i = 0; i < args.size(); ++i) { + if (m_util.is_true(args[i].first)) { + k -= args[i].second; + std::swap(args[i], args[args.size()-1]); + args.pop_back(); + --i; + } + else if (m_util.is_false(args[i].first)) { + std::swap(args[i], args[args.size()-1]); + args.pop_back(); + --i; + } + } + // sort and coalesce arguments: + typename PBU::compare cmp; + std::sort(args.begin(), args.end(), cmp); + + // coallesce + unsigned i, j; + for (i = 0, j = 1; j < args.size(); ++j) { + if (args[i].first == args[j].first) { + args[i].second += args[j].second; + } + else { + ++i; + args[i] = args[j]; + } + } + args.resize(i+1); + + // remove 0s. + for (i = 0, j = 0; j < args.size(); ++j) { + if (!args[j].second.is_zero()) { + if (i != j) { + args[i] = args[j]; + } + ++i; + } + } + args.resize(i); + TRACE("pb_verbose", display(tout << "post-unique:", args, k, is_eq);); +} + +template +lbool pb_rewriter_util::normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { + TRACE("pb_verbose", display(tout << "pre-normalize:", args, k, is_eq);); + + DEBUG_CODE( + bool found = false; + for (unsigned i = 0; !found && i < args.size(); ++i) { + found = args[i].second.is_zero(); + } + if (found) display(verbose_stream(), args, k, is_eq); + SASSERT(!found);); + + // + // Ensure all coefficients are positive: + // c*l + y >= k + // <=> + // c*(1-~l) + y >= k + // <=> + // c - c*~l + y >= k + // <=> + // -c*~l + y >= k - c + // + typename PBU::numeral sum(0); + for (unsigned i = 0; i < args.size(); ++i) { + typename PBU::numeral c = args[i].second; + if (c.is_neg()) { + args[i].second = -c; + args[i].first = m_util.negate(args[i].first); + k -= c; + } + sum += args[i].second; + } + // detect tautologies: + if (!is_eq && k <= PBU::numeral::zero()) { + args.reset(); + k = PBU::numeral::zero(); + return l_true; + } + if (is_eq && k.is_zero() && args.empty()) { + return l_true; + } + + // detect infeasible constraints: + if (sum < k) { + args.reset(); + k = PBU::numeral::one(); + return l_false; + } + + if (is_eq && k == sum) { + for (unsigned i = 0; i < args.size(); ++i) { + args[i].second = PBU::numeral::one(); + } + typename PBU::numeral num(args.size()); + k = num; + return l_undef; + } + + bool all_int = true; + for (unsigned i = 0; all_int && i < args.size(); ++i) { + all_int = args[i].second.is_int(); + } + + if (!all_int) { + // normalize to integers. + typename PBU::numeral d(denominator(k)); + for (unsigned i = 0; i < args.size(); ++i) { + d = lcm(d, denominator(args[i].second)); + } + SASSERT(!d.is_one()); + k *= d; + for (unsigned i = 0; i < args.size(); ++i) { + args[i].second *= d; + } + } + + if (is_eq) { + TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq);); + return l_undef; + } + + // Ensure the largest coefficient is not larger than k: + sum = PBU::numeral::zero(); + for (unsigned i = 0; i < args.size(); ++i) { + typename PBU::numeral c = args[i].second; + if (c > k) { + args[i].second = k; + } + sum += args[i].second; + } + SASSERT(!args.empty()); + + // normalize tight inequalities to unit coefficients. + if (sum == k) { + for (unsigned i = 0; i < args.size(); ++i) { + args[i].second = PBU::numeral::one(); + } + typename PBU::numeral num(args.size()); + k = num; + } + + // apply cutting plane reduction: + typename PBU::numeral g(0); + for (unsigned i = 0; !g.is_one() && i < args.size(); ++i) { + typename PBU::numeral c = args[i].second; + if (c != k) { + if (g.is_zero()) { + g = c; + } + else { + g = gcd(g, c); + } + } + } + if (g.is_zero()) { + // all coefficients are equal to k. + for (unsigned i = 0; i < args.size(); ++i) { + SASSERT(args[i].second == k); + args[i].second = PBU::numeral::one(); + } + k = PBU::numeral::one(); + } + else if (g > PBU::numeral::one()) { + + // + // Example 5x + 5y + 2z + 2u >= 5 + // becomes 3x + 3y + z + u >= 3 + // + typename PBU::numeral k_new = div(k, g); + if (!(k % g).is_zero()) { // k_new is the ceiling of k / g. + k_new++; + } + for (unsigned i = 0; i < args.size(); ++i) { + SASSERT(args[i].second.is_pos()); + typename PBU::numeral c = args[i].second; + if (c == k) { + c = k_new; + } + else { + c = div(c, g); + } + args[i].second = c; + SASSERT(args[i].second.is_pos()); + } + k = k_new; + } + // + // normalize coefficients that fall within a range + // k/n <= ... < k/(n-1) for some n = 1,2,... + // + // e.g, k/n <= min <= max < k/(n-1) + // k/min <= n, n-1 < k/max + // . floor(k/max) = ceil(k/min) - 1 + // . floor(k/max) < k/max + // + // example: k = 5, min = 3, max = 4: 5/3 -> 2 5/4 -> 1, n = 2 + // replace all coefficients by 1, and k by 2. + // + if (!k.is_one()) { + typename PBU::numeral min = args[0].second, max = args[0].second; + for (unsigned i = 1; i < args.size(); ++i) { + if (args[i].second < min) min = args[i].second; + if (args[i].second > max) max = args[i].second; + } + SASSERT(min.is_pos()); + typename PBU::numeral n0 = k/max; + typename PBU::numeral n1 = floor(n0); + typename PBU::numeral n2 = ceil(k/min) - PBU::numeral::one(); + if (n1 == n2 && !n0.is_int()) { + IF_VERBOSE(3, display(verbose_stream() << "set cardinality\n", args, k, is_eq);); + + for (unsigned i = 0; i < args.size(); ++i) { + args[i].second = PBU::numeral::one(); + } + k = n1 + PBU::numeral::one(); + } + } + TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq);); + return l_undef; +} + +template +void pb_rewriter_util::prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) { + if (is_eq) { + return; + } + typename PBU::numeral nlt(0); + unsigned occ = 0; + for (unsigned i = 0; nlt < k && i < args.size(); ++i) { + if (args[i].second < k) { + nlt += args[i].second; + ++occ; + } + } + if (0 < occ && nlt < k) { + for (unsigned i = 0; i < args.size(); ++i) { + if (args[i].second < k) { + args[i] = args.back(); + args.pop_back(); + --i; + } + } + unique(args, k, is_eq); + normalize(args, k, is_eq); + } +} + +#endif diff --git a/src/ast/rewriter/poly_rewriter.h b/src/ast/rewriter/poly_rewriter.h index 6268c761d..90a807874 100644 --- a/src/ast/rewriter/poly_rewriter.h +++ b/src/ast/rewriter/poly_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _POLY_REWRITER_H_ -#define _POLY_REWRITER_H_ +#ifndef POLY_REWRITER_H_ +#define POLY_REWRITER_H_ #include"ast.h" #include"obj_hashtable.h" diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index 0a00a7199..63bdbd519 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -31,6 +31,8 @@ void poly_rewriter::updt_params(params_ref const & _p) { m_hoist_mul = p.hoist_mul(); m_hoist_cmul = p.hoist_cmul(); m_som_blowup = p.som_blowup(); + if (!m_flat) m_som = false; + if (m_som) m_hoist_mul = false; } template diff --git a/src/ast/rewriter/quant_hoist.h b/src/ast/rewriter/quant_hoist.h index 50cbd1ba4..90e6ec7ad 100644 --- a/src/ast/rewriter/quant_hoist.h +++ b/src/ast/rewriter/quant_hoist.h @@ -19,8 +19,8 @@ Revision History: --*/ -#ifndef __QUANTIFIER_HOISTER_H_ -#define __QUANTIFIER_HOISTER_H_ +#ifndef QUANTIFIER_HOISTER_H_ +#define QUANTIFIER_HOISTER_H_ #include "ast.h" diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index f83277bdf..812b07f96 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _REWRITER_H_ -#define _REWRITER_H_ +#ifndef REWRITER_H_ +#define REWRITER_H_ #include"ast.h" #include"rewriter_types.h" diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index 92f06679d..c2e02a1cd 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -324,7 +324,7 @@ void rewriter_tpl::process_app(app * t, frame & fr) { } result_stack().push_back(def); TRACE("get_macro", tout << "bindings:\n"; - for (unsigned i = 0; i < m_bindings.size(); i++) tout << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n";); + for (unsigned j = 0; j < m_bindings.size(); j++) tout << j << ": " << mk_ismt2_pp(m_bindings[j], m()) << "\n";); begin_scope(); m_num_qvars = 0; m_root = def; @@ -578,6 +578,8 @@ void rewriter_tpl::resume_core(expr_ref & result, proof_ref & result_pr) while (!frame_stack().empty()) { if (m_cancel) throw rewriter_exception(Z3_CANCELED_MSG); + if (!m().limit().inc()) + throw rewriter_exception(Z3_MAX_RESOURCE_MSG); SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size()); frame & fr = frame_stack().back(); expr * t = fr.m_curr; diff --git a/src/ast/rewriter/rewriter_types.h b/src/ast/rewriter/rewriter_types.h index d42d6e2bc..f5947eb92 100644 --- a/src/ast/rewriter/rewriter_types.h +++ b/src/ast/rewriter/rewriter_types.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _REWRITER_TYPES_H_ -#define _REWRITER_TYPES_H_ +#ifndef REWRITER_TYPES_H_ +#define REWRITER_TYPES_H_ #include"z3_exception.h" #include"common_msgs.h" diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index f35c3b134..b7197d239 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -25,6 +25,7 @@ Notes: #include"array_rewriter.h" #include"fpa_rewriter.h" #include"dl_rewriter.h" +#include"pb_rewriter.h" #include"rewriter_def.h" #include"expr_substitution.h" #include"ast_smt2_pp.h" @@ -41,6 +42,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { datatype_rewriter m_dt_rw; fpa_rewriter m_f_rw; dl_rewriter m_dl_rw; + pb_rewriter m_pb_rw; arith_util m_a_util; bv_util m_bv_util; unsigned long long m_max_memory; // in bytes @@ -196,6 +198,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg { return m_f_rw.mk_app_core(f, num, args, result); if (fid == m_dl_rw.get_fid()) return m_dl_rw.mk_app_core(f, num, args, result); + if (fid == m_pb_rw.get_fid()) + return m_pb_rw.mk_app_core(f, num, args, result); return BR_FAILED; } @@ -645,6 +649,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_dt_rw(m), m_f_rw(m, p), m_dl_rw(m), + m_pb_rw(m), m_a_util(m), m_bv_util(m), m_used_dependencies(m), diff --git a/src/ast/rewriter/th_rewriter.h b/src/ast/rewriter/th_rewriter.h index b25558dd9..627ce0694 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _TH_REWRITER_H_ -#define _TH_REWRITER_H_ +#ifndef TH_REWRITER_H_ +#define TH_REWRITER_H_ #include"ast.h" #include"rewriter_types.h" diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index 930267dad..1d01ef85c 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -164,7 +164,7 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";); } -static void get_free_vars_offset(ast_mark& mark, ptr_vector& todo, unsigned offset, expr* e, ptr_vector& sorts) { +static void get_free_vars_offset(expr_sparse_mark& mark, ptr_vector& todo, unsigned offset, expr* e, ptr_vector& sorts) { todo.push_back(e); while (!todo.empty()) { e = todo.back(); @@ -176,7 +176,7 @@ static void get_free_vars_offset(ast_mark& mark, ptr_vector& todo, unsigne switch(e->get_kind()) { case AST_QUANTIFIER: { quantifier* q = to_quantifier(e); - ast_mark mark1; + expr_sparse_mark mark1; ptr_vector todo1; get_free_vars_offset(mark1, todo1, offset+q->get_num_decls(), q->get_expr(), sorts); break; @@ -210,11 +210,33 @@ static void get_free_vars_offset(ast_mark& mark, ptr_vector& todo, unsigne void get_free_vars(expr* e, ptr_vector& sorts) { - ast_mark mark; + expr_sparse_mark mark; ptr_vector todo; get_free_vars_offset(mark, todo, 0, e, sorts); } -void get_free_vars(ast_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts) { +void get_free_vars(expr_sparse_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts) { get_free_vars_offset(mark, todo, 0, e, sorts); } + +void expr_free_vars::reset() { + m_mark.reset(); + m_sorts.reset(); + SASSERT(m_todo.empty()); +} + +void expr_free_vars::set_default_sort(sort *s) { + for (unsigned i = 0; i < m_sorts.size(); ++i) { + if (!m_sorts[i]) m_sorts[i] = s; + } +} + +void expr_free_vars::operator()(expr* e) { + reset(); + get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts); +} + +void expr_free_vars::accumulate(expr* e) { + SASSERT(m_todo.empty()); + get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts); +} diff --git a/src/ast/rewriter/var_subst.h b/src/ast/rewriter/var_subst.h index ffc21e691..9d04cebe3 100644 --- a/src/ast/rewriter/var_subst.h +++ b/src/ast/rewriter/var_subst.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _VAR_SUBST_H_ -#define _VAR_SUBST_H_ +#ifndef VAR_SUBST_H_ +#define VAR_SUBST_H_ #include"rewriter.h" #include"used_vars.h" @@ -81,9 +81,23 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref Return the sorts of the free variables. */ -void get_free_vars(expr* e, ptr_vector& sorts); -void get_free_vars(ast_mark& mark, ptr_vector& todo, expr* e, ptr_vector& sorts); +class expr_free_vars { + expr_sparse_mark m_mark; + ptr_vector m_sorts; + ptr_vector m_todo; +public: + void reset(); + void operator()(expr* e); + void accumulate(expr* e); + bool empty() const { return m_sorts.empty(); } + unsigned size() const { return m_sorts.size(); } + sort* operator[](unsigned idx) const { return m_sorts[idx]; } + bool contains(unsigned idx) const { return idx < m_sorts.size() && m_sorts[idx] != 0; } + void set_default_sort(sort* s); + void reverse() { m_sorts.reverse(); } + sort*const* c_ptr() const { return m_sorts.c_ptr(); } +}; #endif diff --git a/src/ast/scoped_proof.h b/src/ast/scoped_proof.h index e23a6d92a..2bbba5122 100644 --- a/src/ast/scoped_proof.h +++ b/src/ast/scoped_proof.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SCOPED_PROOF__H_ -#define _SCOPED_PROOF__H_ +#ifndef SCOPED_PROOF_H_ +#define SCOPED_PROOF_H_ #include "ast.h" diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 1bb3f3581..c71c7317e 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SEQ_DECL_PLUGIN_H_ -#define _SEQ_DECL_PLUGIN_H_ +#ifndef SEQ_DECL_PLUGIN_H_ +#define SEQ_DECL_PLUGIN_H_ #include "ast.h" @@ -119,5 +119,5 @@ public: }; -#endif /* _SEQ_DECL_PLUGIN_H_ */ +#endif /* SEQ_DECL_PLUGIN_H_ */ diff --git a/src/ast/shared_occs.h b/src/ast/shared_occs.h index e135fea84..1566098aa 100644 --- a/src/ast/shared_occs.h +++ b/src/ast/shared_occs.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SHARED_OCCS_H_ -#define _SHARED_OCCS_H_ +#ifndef SHARED_OCCS_H_ +#define SHARED_OCCS_H_ #include"ast.h" #include"obj_hashtable.h" @@ -75,7 +75,7 @@ public: iterator end_shared() const { return m_shared.end(); } void reset(); void cleanup(); - void display(std::ostream & out, ast_manager & m) const; + void display(std::ostream & out, ast_manager & mgr) const; }; #endif diff --git a/src/ast/simplifier/arith_simplifier_params.h b/src/ast/simplifier/arith_simplifier_params.h index 109f73307..2ff8fe2c0 100644 --- a/src/ast/simplifier/arith_simplifier_params.h +++ b/src/ast/simplifier/arith_simplifier_params.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ARITH_SIMPLIFIER_PARAMS_H_ -#define _ARITH_SIMPLIFIER_PARAMS_H_ +#ifndef ARITH_SIMPLIFIER_PARAMS_H_ +#define ARITH_SIMPLIFIER_PARAMS_H_ #include"params.h" @@ -32,5 +32,5 @@ struct arith_simplifier_params { void updt_params(params_ref const & _p); }; -#endif /* _ARITH_SIMPLIFIER_PARAMS_H_ */ +#endif /* ARITH_SIMPLIFIER_PARAMS_H_ */ diff --git a/src/ast/simplifier/arith_simplifier_plugin.h b/src/ast/simplifier/arith_simplifier_plugin.h index 4b8f579c0..e6181e211 100644 --- a/src/ast/simplifier/arith_simplifier_plugin.h +++ b/src/ast/simplifier/arith_simplifier_plugin.h @@ -14,8 +14,8 @@ Author: Leonardo (leonardo) 2008-01-08 --*/ -#ifndef _ARITH_SIMPLIFIER_PLUGIN_H_ -#define _ARITH_SIMPLIFIER_PLUGIN_H_ +#ifndef ARITH_SIMPLIFIER_PLUGIN_H_ +#define ARITH_SIMPLIFIER_PLUGIN_H_ #include"basic_simplifier_plugin.h" #include"poly_simplifier_plugin.h" @@ -93,4 +93,4 @@ public: }; -#endif /* _ARITH_SIMPLIFIER_PLUGIN_H_ */ +#endif /* ARITH_SIMPLIFIER_PLUGIN_H_ */ diff --git a/src/ast/simplifier/array_simplifier_params.h b/src/ast/simplifier/array_simplifier_params.h index 2f6fa720b..c62b990b9 100644 --- a/src/ast/simplifier/array_simplifier_params.h +++ b/src/ast/simplifier/array_simplifier_params.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ARRAY_SIMPLIFIER_PARAMS_H_ -#define _ARRAY_SIMPLIFIER_PARAMS_H_ +#ifndef ARRAY_SIMPLIFIER_PARAMS_H_ +#define ARRAY_SIMPLIFIER_PARAMS_H_ #include"params.h" @@ -32,5 +32,5 @@ struct array_simplifier_params { void updt_params(params_ref const & _p); }; -#endif /* _ARITH_SIMPLIFIER_PARAMS_H_ */ +#endif /* ARITH_SIMPLIFIER_PARAMS_H_ */ diff --git a/src/ast/simplifier/array_simplifier_plugin.cpp b/src/ast/simplifier/array_simplifier_plugin.cpp index 221d2a209..85ef2e92f 100644 --- a/src/ast/simplifier/array_simplifier_plugin.cpp +++ b/src/ast/simplifier/array_simplifier_plugin.cpp @@ -332,6 +332,7 @@ lbool array_simplifier_plugin::eq_default(expr* def, unsigned arity, unsigned nu for (unsigned i = 0; i < num_st; ++i) { all_eq &= (st[i][arity] == def); all_diseq &= m_manager.is_unique_value(st[i][arity]) && (st[i][arity] != def); + TRACE("array_simplifier", tout << m_manager.is_unique_value(st[i][arity]) << " " << mk_pp(st[i][arity], m_manager) << "\n";); } if (all_eq) { return l_true; @@ -350,6 +351,12 @@ bool array_simplifier_plugin::insert_table(expr* def, unsigned arity, unsigned n return false; } } + TRACE("array_simplifier", tout << "inserting: "; + for (unsigned j = 0; j < arity; ++j) { + tout << mk_pp(st[i][j], m_manager) << " "; + } + tout << " |-> " << mk_pp(def, m_manager) << "\n"; + ); args_entry e(arity, st[i]); table.insert_if_not_there(e); } @@ -424,7 +431,8 @@ bool array_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & resul lbool eq = eq_stores(c1, arity2, st1.size(), st1.c_ptr(), st2.size(), st2.c_ptr()); TRACE("array_simplifier", tout << mk_pp(lhs, m_manager) << " = " - << mk_pp(rhs, m_manager) << " := " << eq << "\n";); + << mk_pp(rhs, m_manager) << " := " << eq << "\n"; + tout << "arity: " << arity1 << "\n";); switch(eq) { case l_false: result = m_manager.mk_false(); diff --git a/src/ast/simplifier/array_simplifier_plugin.h b/src/ast/simplifier/array_simplifier_plugin.h index 572da9a17..34db04b67 100644 --- a/src/ast/simplifier/array_simplifier_plugin.h +++ b/src/ast/simplifier/array_simplifier_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ARRAY_SIMPLIFIER_PLUGIN_H_ -#define _ARRAY_SIMPLIFIER_PLUGIN_H_ +#ifndef ARRAY_SIMPLIFIER_PLUGIN_H_ +#define ARRAY_SIMPLIFIER_PLUGIN_H_ #include"ast.h" #include"map.h" @@ -150,5 +150,5 @@ private: }; -#endif /* _ARRAY_SIMPLIFIER_PLUGIN_H_ */ +#endif /* ARRAY_SIMPLIFIER_PLUGIN_H_ */ diff --git a/src/ast/simplifier/base_simplifier.h b/src/ast/simplifier/base_simplifier.h index 4afe5c9d7..cb630cb7f 100644 --- a/src/ast/simplifier/base_simplifier.h +++ b/src/ast/simplifier/base_simplifier.h @@ -16,25 +16,36 @@ Author: Notes: --*/ -#ifndef _BASE_SIMPLIFIER_H_ -#define _BASE_SIMPLIFIER_H_ +#ifndef BASE_SIMPLIFIER_H_ +#define BASE_SIMPLIFIER_H_ #include"expr_map.h" +#include"ast_pp.h" /** \brief Implements basic functionality used by expression simplifiers. */ class base_simplifier { protected: - ast_manager & m_manager; + ast_manager & m; expr_map m_cache; ptr_vector m_todo; - void cache_result(expr * n, expr * r, proof * p) { m_cache.insert(n, r, p); } + void cache_result(expr * n, expr * r, proof * p) { + m_cache.insert(n, r, p); + CTRACE("simplifier", !is_rewrite_proof(n, r, p), + tout << mk_pp(n, m) << "\n"; + tout << mk_pp(r, m) << "\n"; + tout << mk_pp(p, m) << "\n";); + SASSERT(is_rewrite_proof(n, r, p)); + } void reset_cache() { m_cache.reset(); } void flush_cache() { m_cache.flush(); } void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); } + void reinitialize() { m_cache.set_store_proofs(m.fine_grain_proofs()); } + + void visit(expr * n, bool & visited) { if (!is_cached(n)) { m_todo.push_back(n); @@ -44,11 +55,22 @@ protected: public: base_simplifier(ast_manager & m): - m_manager(m), + m(m), m_cache(m, m.fine_grain_proofs()) { } bool is_cached(expr * n) const { return m_cache.contains(n); } - ast_manager & get_manager() { return m_manager; } + ast_manager & get_manager() { return m; } + + bool is_rewrite_proof(expr* n, expr* r, proof* p) { + if (p && + !m.is_undef_proof(p) && + !(m.has_fact(p) && + (m.is_eq(m.get_fact(p)) || m.is_oeq(m.get_fact(p)) || m.is_iff(m.get_fact(p))) && + to_app(m.get_fact(p))->get_arg(0) == n && + to_app(m.get_fact(p))->get_arg(1) == r)) return false; + + return (!m.fine_grain_proofs() || p || (n == r)); + } }; -#endif /* _BASE_SIMPLIFIER_H_ */ +#endif /* BASE_SIMPLIFIER_H_ */ diff --git a/src/ast/simplifier/basic_simplifier_plugin.h b/src/ast/simplifier/basic_simplifier_plugin.h index d92e09d1d..d39d6badb 100644 --- a/src/ast/simplifier/basic_simplifier_plugin.h +++ b/src/ast/simplifier/basic_simplifier_plugin.h @@ -14,8 +14,8 @@ Author: Leonardo (leonardo) 2008-01-07 --*/ -#ifndef _BASIC_SIMPLIFIER_PLUGIN_H_ -#define _BASIC_SIMPLIFIER_PLUGIN_H_ +#ifndef BASIC_SIMPLIFIER_PLUGIN_H_ +#define BASIC_SIMPLIFIER_PLUGIN_H_ #include"simplifier_plugin.h" @@ -75,4 +75,4 @@ struct expr_lt_proc { } }; -#endif /* _BASIC_SIMPLIFIER_PLUGIN_H_ */ +#endif /* BASIC_SIMPLIFIER_PLUGIN_H_ */ diff --git a/src/ast/simplifier/bit2int.h b/src/ast/simplifier/bit2int.h index 36351337b..6277a83ae 100644 --- a/src/ast/simplifier/bit2int.h +++ b/src/ast/simplifier/bit2int.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _BIT2INT_H_ -#define _BIT2INT_H_ +#ifndef BIT2INT_H_ +#define BIT2INT_H_ #include"bv_decl_plugin.h" #include"arith_decl_plugin.h" @@ -92,5 +92,5 @@ public: void operator()(expr * m, expr_ref & result, proof_ref& p); }; -#endif /* _BIT2INT_H_ */ +#endif /* BIT2INT_H_ */ diff --git a/src/ast/simplifier/bv_elim.cpp b/src/ast/simplifier/bv_elim.cpp index 5c19a245e..8dc2671ca 100644 --- a/src/ast/simplifier/bv_elim.cpp +++ b/src/ast/simplifier/bv_elim.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "bv_elim.h" #include "bv_decl_plugin.h" #include "var_subst.h" @@ -100,11 +106,11 @@ bool bv_elim_star::visit_quantifier(quantifier* q) { } void bv_elim_star::reduce1_quantifier(quantifier* q) { - quantifier_ref r(m_manager); - proof_ref pr(m_manager); + quantifier_ref r(m); + proof_ref pr(m); m_bv_elim.elim(q, r); - if (m_manager.fine_grain_proofs()) { - pr = m_manager.mk_rewrite(q, r.get()); + if (m.fine_grain_proofs()) { + pr = m.mk_rewrite(q, r.get()); } else { pr = 0; diff --git a/src/ast/simplifier/bv_elim.h b/src/ast/simplifier/bv_elim.h index bca017db7..3bebcfef1 100644 --- a/src/ast/simplifier/bv_elim.h +++ b/src/ast/simplifier/bv_elim.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _BV_ELIM_H_ -#define _BV_ELIM_H_ +#ifndef BV_ELIM_H_ +#define BV_ELIM_H_ #include "ast.h" #include "simplifier.h" @@ -41,5 +41,5 @@ public: virtual ~bv_elim_star() {} }; -#endif /* _BV_ELIM_H_ */ +#endif /* BV_ELIM_H_ */ diff --git a/src/ast/simplifier/bv_simplifier_params.h b/src/ast/simplifier/bv_simplifier_params.h index 5f5832235..50015b7ca 100644 --- a/src/ast/simplifier/bv_simplifier_params.h +++ b/src/ast/simplifier/bv_simplifier_params.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _BV_SIMPLIFIER_PARAMS_H_ -#define _BV_SIMPLIFIER_PARAMS_H_ +#ifndef BV_SIMPLIFIER_PARAMS_H_ +#define BV_SIMPLIFIER_PARAMS_H_ #include"params.h" @@ -32,5 +32,5 @@ struct bv_simplifier_params { void updt_params(params_ref const & _p); }; -#endif /* _BV_SIMPLIFIER_PARAMS_H_ */ +#endif /* BV_SIMPLIFIER_PARAMS_H_ */ diff --git a/src/ast/simplifier/bv_simplifier_plugin.h b/src/ast/simplifier/bv_simplifier_plugin.h index 36e773de0..484edca95 100644 --- a/src/ast/simplifier/bv_simplifier_plugin.h +++ b/src/ast/simplifier/bv_simplifier_plugin.h @@ -14,8 +14,8 @@ Author: Leonardo (leonardo) 2008-01-08 --*/ -#ifndef _BV_SIMPLIFIER_PLUGIN_H_ -#define _BV_SIMPLIFIER_PLUGIN_H_ +#ifndef BV_SIMPLIFIER_PLUGIN_H_ +#define BV_SIMPLIFIER_PLUGIN_H_ #include"basic_simplifier_plugin.h" #include"poly_simplifier_plugin.h" @@ -184,4 +184,4 @@ public: }; -#endif /* _BV_SIMPLIFIER_PLUGIN_H_ */ +#endif /* BV_SIMPLIFIER_PLUGIN_H_ */ diff --git a/src/ast/simplifier/datatype_simplifier_plugin.cpp b/src/ast/simplifier/datatype_simplifier_plugin.cpp index 129c0e34b..b434a8bd0 100644 --- a/src/ast/simplifier/datatype_simplifier_plugin.cpp +++ b/src/ast/simplifier/datatype_simplifier_plugin.cpp @@ -81,6 +81,8 @@ bool datatype_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr * } UNREACHABLE(); } + case OP_DT_UPDATE_FIELD: + return false; default: UNREACHABLE(); } diff --git a/src/ast/simplifier/datatype_simplifier_plugin.h b/src/ast/simplifier/datatype_simplifier_plugin.h index 2a0de8aae..b675bf92e 100644 --- a/src/ast/simplifier/datatype_simplifier_plugin.h +++ b/src/ast/simplifier/datatype_simplifier_plugin.h @@ -14,8 +14,8 @@ Author: nbjorner 2008-11-6 --*/ -#ifndef _DATATYPE_SIMPLIFIER_PLUGIN_H_ -#define _DATATYPE_SIMPLIFIER_PLUGIN_H_ +#ifndef DATATYPE_SIMPLIFIER_PLUGIN_H_ +#define DATATYPE_SIMPLIFIER_PLUGIN_H_ #include"basic_simplifier_plugin.h" #include"datatype_decl_plugin.h" @@ -39,4 +39,4 @@ public: }; -#endif /* _DATATYPE_SIMPLIFIER_PLUGIN_H_ */ +#endif /* DATATYPE_SIMPLIFIER_PLUGIN_H_ */ diff --git a/src/ast/simplifier/distribute_forall.h b/src/ast/simplifier/distribute_forall.h index 8816bf22e..4c2eefb56 100644 --- a/src/ast/simplifier/distribute_forall.h +++ b/src/ast/simplifier/distribute_forall.h @@ -18,8 +18,8 @@ Revision History: Christoph Wintersteiger 2010-04-06: Added implementation --*/ -#ifndef _DISTRIBUTE_FORALL_H_ -#define _DISTRIBUTE_FORALL_H_ +#ifndef DISTRIBUTE_FORALL_H_ +#define DISTRIBUTE_FORALL_H_ #include"ast.h" #include"basic_simplifier_plugin.h" @@ -79,4 +79,4 @@ protected: void flush_cache() { m_cache.cleanup(); } }; -#endif /* _DISTRIBUTE_FORALL_H_ */ +#endif /* DISTRIBUTE_FORALL_H_ */ diff --git a/src/ast/simplifier/elim_bounds.cpp b/src/ast/simplifier/elim_bounds.cpp index ce15d9eb1..41ce18549 100644 --- a/src/ast/simplifier/elim_bounds.cpp +++ b/src/ast/simplifier/elim_bounds.cpp @@ -203,20 +203,20 @@ void elim_bounds_star::reduce1_quantifier(quantifier * q) { cache_result(q, q, 0); return; } - quantifier_ref new_q(m_manager); + quantifier_ref new_q(m); expr * new_body = 0; proof * new_pr; get_cached(q->get_expr(), new_body, new_pr); - new_q = m_manager.update_quantifier(q, new_body); - expr_ref r(m_manager); + new_q = m.update_quantifier(q, new_body); + expr_ref r(m); m_elim(new_q, r); if (q == r.get()) { cache_result(q, q, 0); return; } - proof_ref pr(m_manager); - if (m_manager.fine_grain_proofs()) - pr = m_manager.mk_rewrite(q, r); // TODO: improve justification + proof_ref pr(m); + if (m.fine_grain_proofs()) + pr = m.mk_rewrite(q, r); // TODO: improve justification cache_result(q, r, pr); } diff --git a/src/ast/simplifier/elim_bounds.h b/src/ast/simplifier/elim_bounds.h index 66b6ed524..f8276c150 100644 --- a/src/ast/simplifier/elim_bounds.h +++ b/src/ast/simplifier/elim_bounds.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ELIM_BOUNDS_H_ -#define _ELIM_BOUNDS_H_ +#ifndef ELIM_BOUNDS_H_ +#define ELIM_BOUNDS_H_ #include"ast.h" #include"arith_decl_plugin.h" @@ -65,5 +65,5 @@ public: virtual ~elim_bounds_star() {} }; -#endif /* _ELIM_BOUNDS_H_ */ +#endif /* ELIM_BOUNDS_H_ */ diff --git a/src/ast/simplifier/fpa_simplifier_plugin.h b/src/ast/simplifier/fpa_simplifier_plugin.h index 08fa6c49b..0ee8debf2 100644 --- a/src/ast/simplifier/fpa_simplifier_plugin.h +++ b/src/ast/simplifier/fpa_simplifier_plugin.h @@ -14,8 +14,8 @@ Author: Christoph (cwinter) 2015-01-14 --*/ -#ifndef _FPA_SIMPLIFIER_PLUGIN_H_ -#define _FPA_SIMPLIFIER_PLUGIN_H_ +#ifndef FPA_SIMPLIFIER_PLUGIN_H_ +#define FPA_SIMPLIFIER_PLUGIN_H_ #include"basic_simplifier_plugin.h" #include"fpa_decl_plugin.h" @@ -36,4 +36,4 @@ public: }; -#endif /* _FPA_SIMPLIFIER_PLUGIN_H_ */ +#endif /* FPA_SIMPLIFIER_PLUGIN_H_ */ diff --git a/src/ast/simplifier/inj_axiom.cpp b/src/ast/simplifier/inj_axiom.cpp index 50d051d2e..5925e23ac 100644 --- a/src/ast/simplifier/inj_axiom.cpp +++ b/src/ast/simplifier/inj_axiom.cpp @@ -114,7 +114,7 @@ bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) { ptr_vector domain; inv_vars.push_back(f); - for (unsigned i = 0; i < inv_vars.size(); ++i) { + for (unsigned i = 0; i < inv_vars.size(); ++i) { domain.push_back(m.get_sort(inv_vars[i])); } sort * d = decl->get_domain(idx); diff --git a/src/ast/simplifier/inj_axiom.h b/src/ast/simplifier/inj_axiom.h index f473d5eae..da441c15b 100644 --- a/src/ast/simplifier/inj_axiom.h +++ b/src/ast/simplifier/inj_axiom.h @@ -16,12 +16,12 @@ Author: Revision History: --*/ -#ifndef _INJ_AXIOM_H_ -#define _INJ_AXIOM_H_ +#ifndef INJ_AXIOM_H_ +#define INJ_AXIOM_H_ #include"ast.h" bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result); -#endif /* _INJ_AXIOM_H_ */ +#endif /* INJ_AXIOM_H_ */ diff --git a/src/ast/simplifier/maximise_ac_sharing.h b/src/ast/simplifier/maximise_ac_sharing.h index bc3b3e67a..bd369387c 100644 --- a/src/ast/simplifier/maximise_ac_sharing.h +++ b/src/ast/simplifier/maximise_ac_sharing.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MAXIMISE_AC_SHARING_H_ -#define _MAXIMISE_AC_SHARING_H_ +#ifndef MAXIMISE_AC_SHARING_H_ +#define MAXIMISE_AC_SHARING_H_ #include"simplifier.h" #include"hashtable.h" @@ -120,5 +120,5 @@ public: maximise_bv_sharing(ast_manager & m); }; -#endif /* _MAXIMISE_AC_SHARING_H_ */ +#endif /* MAXIMISE_AC_SHARING_H_ */ diff --git a/src/ast/simplifier/poly_simplifier_plugin.cpp b/src/ast/simplifier/poly_simplifier_plugin.cpp index d64123e7b..c5dc275fd 100644 --- a/src/ast/simplifier/poly_simplifier_plugin.cpp +++ b/src/ast/simplifier/poly_simplifier_plugin.cpp @@ -69,14 +69,20 @@ expr * poly_simplifier_plugin::mk_mul(unsigned num_args, expr * const * args) { } expr * poly_simplifier_plugin::mk_mul(numeral const & c, expr * body) { - numeral c_prime; + numeral c_prime, d; c_prime = norm(c); if (c_prime.is_zero()) return 0; if (body == 0) return mk_numeral(c_prime); if (c_prime.is_one()) - return body; + return body; + if (is_numeral(body, d)) { + c_prime = norm(c_prime*d); + if (c_prime.is_zero()) + return 0; + return mk_numeral(c_prime); + } set_curr_sort(body); expr * args[2] = { mk_numeral(c_prime), body }; return mk_mul(2, args); diff --git a/src/ast/simplifier/poly_simplifier_plugin.h b/src/ast/simplifier/poly_simplifier_plugin.h index ec4aa336e..4e6ae9c15 100644 --- a/src/ast/simplifier/poly_simplifier_plugin.h +++ b/src/ast/simplifier/poly_simplifier_plugin.h @@ -14,8 +14,8 @@ Author: Leonardo (leonardo) 2008-01-08 --*/ -#ifndef _POLY_SIMPLIFIER_PLUGIN_H_ -#define _POLY_SIMPLIFIER_PLUGIN_H_ +#ifndef POLY_SIMPLIFIER_PLUGIN_H_ +#define POLY_SIMPLIFIER_PLUGIN_H_ #include "simplifier_plugin.h" @@ -152,4 +152,4 @@ public: #endif }; -#endif /* _POLY_SIMPLIFIER_PLUGIN_H_ */ +#endif /* POLY_SIMPLIFIER_PLUGIN_H_ */ diff --git a/src/ast/simplifier/pull_ite_tree.cpp b/src/ast/simplifier/pull_ite_tree.cpp index 080dd5545..05bb3d885 100644 --- a/src/ast/simplifier/pull_ite_tree.cpp +++ b/src/ast/simplifier/pull_ite_tree.cpp @@ -187,7 +187,7 @@ pull_ite_tree_star::pull_ite_tree_star(ast_manager & m, simplifier & s): bool pull_ite_tree_star::get_subst(expr * n, expr_ref & r, proof_ref & p) { if (is_app(n) && is_target(to_app(n))) { - app_ref tmp(m_manager); + app_ref tmp(m); m_proc(to_app(n), tmp, p); r = tmp; return true; @@ -199,10 +199,10 @@ bool pull_cheap_ite_tree_star::is_target(app * n) const { bool r = n->get_num_args() == 2 && n->get_family_id() != null_family_id && - m_manager.is_bool(n) && - (m_manager.is_value(n->get_arg(0)) || m_manager.is_value(n->get_arg(1))) && - (m_manager.is_term_ite(n->get_arg(0)) || m_manager.is_term_ite(n->get_arg(1))); - TRACE("pull_ite_target", tout << mk_pp(n, m_manager) << "\nresult: " << r << "\n";); + m.is_bool(n) && + (m.is_value(n->get_arg(0)) || m.is_value(n->get_arg(1))) && + (m.is_term_ite(n->get_arg(0)) || m.is_term_ite(n->get_arg(1))); + TRACE("pull_ite_target", tout << mk_pp(n, m) << "\nresult: " << r << "\n";); return r; } diff --git a/src/ast/simplifier/pull_ite_tree.h b/src/ast/simplifier/pull_ite_tree.h index 9350b7339..14c447697 100644 --- a/src/ast/simplifier/pull_ite_tree.h +++ b/src/ast/simplifier/pull_ite_tree.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _PULL_ITE_TREE_H_ -#define _PULL_ITE_TREE_H_ +#ifndef PULL_ITE_TREE_H_ +#define PULL_ITE_TREE_H_ #include"ast.h" #include"simplifier.h" @@ -97,5 +97,5 @@ public: virtual bool is_target(app * n) const; }; -#endif /* _PULL_ITE_TREE_H_ */ +#endif /* PULL_ITE_TREE_H_ */ diff --git a/src/ast/simplifier/push_app_ite.cpp b/src/ast/simplifier/push_app_ite.cpp index cec84d1f5..e33f0d094 100644 --- a/src/ast/simplifier/push_app_ite.cpp +++ b/src/ast/simplifier/push_app_ite.cpp @@ -34,7 +34,7 @@ push_app_ite::~push_app_ite() { int push_app_ite::has_ite_arg(unsigned num_args, expr * const * args) { for (unsigned i = 0; i < num_args; i++) - if (m_manager.is_ite(args[i])) + if (m.is_ite(args[i])) return i; return -1; } @@ -53,10 +53,10 @@ void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * arg expr ** args_prime = const_cast(args); expr * old = args_prime[ite_arg_idx]; args_prime[ite_arg_idx] = t; - expr_ref t_new(m_manager); + expr_ref t_new(m); apply(decl, num_args, args_prime, t_new); args_prime[ite_arg_idx] = e; - expr_ref e_new(m_manager); + expr_ref e_new(m); apply(decl, num_args, args_prime, e_new); args_prime[ite_arg_idx] = old; expr * new_args[3] = { c, t_new, e_new }; @@ -67,11 +67,11 @@ void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * arg \brief Default (conservative) implementation. Return true if there one and only one ite-term argument. */ bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) { - if (m_manager.is_ite(decl)) + if (m.is_ite(decl)) return false; bool found_ite = false; for (unsigned i = 0; i < num_args; i++) { - if (m_manager.is_ite(args[i]) && !m_manager.is_bool(args[i])) { + if (m.is_ite(args[i]) && !m.is_bool(args[i])) { if (found_ite) { if (m_conservative) return false; @@ -83,7 +83,7 @@ bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * } CTRACE("push_app_ite", found_ite, tout << "found target for push app ite:\n"; tout << decl->get_name(); - for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m_manager); + for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m); tout << "\n";); return found_ite; } @@ -94,19 +94,19 @@ void push_app_ite::operator()(expr * s, expr_ref & r, proof_ref & p) { reduce_core(s); get_cached(s, result, result_proof); r = result; - switch (m_manager.proof_mode()) { + switch (m.proof_mode()) { case PGM_DISABLED: - p = m_manager.mk_undef_proof(); + p = m.mk_undef_proof(); break; case PGM_COARSE: if (result == s) - p = m_manager.mk_reflexivity(s); + p = m.mk_reflexivity(s); else - p = m_manager.mk_rewrite_star(s, result, 0, 0); + p = m.mk_rewrite_star(s, result, 0, 0); break; case PGM_FINE: if (result == s) - p = m_manager.mk_reflexivity(s); + p = m.mk_reflexivity(s); else p = result_proof; break; @@ -171,24 +171,24 @@ void push_app_ite::reduce1_app(app * n) { m_args.reset(); func_decl * decl = n->get_decl(); - proof_ref p1(m_manager); + proof_ref p1(m); get_args(n, m_args, p1); - expr_ref r(m_manager); + expr_ref r(m); if (is_target(decl, m_args.size(), m_args.c_ptr())) apply(decl, m_args.size(), m_args.c_ptr(), r); else mk_app(decl, m_args.size(), m_args.c_ptr(), r); - if (!m_manager.fine_grain_proofs()) + if (!m.fine_grain_proofs()) cache_result(n, r, 0); else { - expr * s = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr()); + expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr()); proof * p; if (n == r) p = 0; else if (r != s) - p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(s, r)); + p = m.mk_transitivity(p1, m.mk_rewrite(s, r)); else p = p1; cache_result(n, r, p); @@ -200,8 +200,8 @@ void push_app_ite::reduce1_quantifier(quantifier * q) { proof * new_body_pr; get_cached(q->get_expr(), new_body, new_body_pr); - quantifier * new_q = m_manager.update_quantifier(q, new_body); - proof * p = q == new_q ? 0 : m_manager.mk_quant_intro(q, new_q, new_body_pr); + quantifier * new_q = m.update_quantifier(q, new_body); + proof * p = q == new_q ? 0 : m.mk_quant_intro(q, new_q, new_body_pr); cache_result(q, new_q, p); } diff --git a/src/ast/simplifier/push_app_ite.h b/src/ast/simplifier/push_app_ite.h index 2d5c6bc28..104a7ea74 100644 --- a/src/ast/simplifier/push_app_ite.h +++ b/src/ast/simplifier/push_app_ite.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _PUSH_APP_ITE_H_ -#define _PUSH_APP_ITE_H_ +#ifndef PUSH_APP_ITE_H_ +#define PUSH_APP_ITE_H_ #include"ast.h" #include"simplifier.h" @@ -59,5 +59,5 @@ public: virtual ~ng_push_app_ite() {} }; -#endif /* _PUSH_APP_ITE_H_ */ +#endif /* PUSH_APP_ITE_H_ */ diff --git a/src/ast/simplifier/simplifier.cpp b/src/ast/simplifier/simplifier.cpp index c9f72f4d5..5358e01e4 100644 --- a/src/ast/simplifier/simplifier.cpp +++ b/src/ast/simplifier/simplifier.cpp @@ -61,16 +61,18 @@ void simplifier::enable_ac_support(bool flag) { */ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) { m_need_reset = true; + reinitialize(); + expr * s_orig = s; expr * old_s; expr * result; proof * result_proof; - switch (m_manager.proof_mode()) { + switch (m.proof_mode()) { case PGM_DISABLED: // proof generation is disabled. reduce_core(s); // after executing reduce_core, the result of the simplification is in the cache get_cached(s, result, result_proof); r = result; - p = m_manager.mk_undef_proof(); + p = m.mk_undef_proof(); break; case PGM_COARSE: // coarse proofs... in this case, we do not produce a step by step (fine grain) proof to show the equivalence (or equisatisfiability) of s an r. m_subst_proofs.reset(); // m_subst_proofs is an auxiliary vector that is used to justify substitutions. See comment on method get_subst. @@ -78,10 +80,10 @@ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) { get_cached(s, result, result_proof); r = result; if (result == s) - p = m_manager.mk_reflexivity(s); + p = m.mk_reflexivity(s); else { remove_duplicates(m_subst_proofs); - p = m_manager.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr()); + p = m.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr()); } break; case PGM_FINE: // fine grain proofs... in this mode, every proof step (or most of them) is described. @@ -90,17 +92,20 @@ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) { // keep simplyfing until no further simplifications are possible. while (s != old_s) { TRACE("simplifier", tout << "simplification pass... " << s->get_id() << "\n";); - TRACE("simplifier_loop", tout << mk_ll_pp(s, m_manager) << "\n";); + TRACE("simplifier_loop", tout << mk_ll_pp(s, m) << "\n";); reduce_core(s); get_cached(s, result, result_proof); - if (result_proof != 0) + SASSERT(is_rewrite_proof(s, result, result_proof)); + if (result_proof != 0) { m_proofs.push_back(result_proof); + } old_s = s; s = result; } SASSERT(s != 0); r = s; - p = m_proofs.empty() ? m_manager.mk_reflexivity(s) : m_manager.mk_transitivity(m_proofs.size(), m_proofs.c_ptr()); + p = m_proofs.empty() ? m.mk_reflexivity(s) : m.mk_transitivity(m_proofs.size(), m_proofs.c_ptr()); + SASSERT(is_rewrite_proof(s_orig, r, p)); break; default: UNREACHABLE(); @@ -259,9 +264,9 @@ void simplifier::reduce1(expr * n) { specific simplifications via plugins. */ void simplifier::reduce1_app(app * n) { - expr_ref r(m_manager); - proof_ref p(m_manager); - TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m_manager) << "\n";); + expr_ref r(m); + proof_ref p(m); + TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m) << "\n";); if (get_subst(n, r, p)) { TRACE("reduce", tout << "applying substitution...\n";); cache_result(n, r, p); @@ -279,7 +284,7 @@ void simplifier::reduce1_app(app * n) { void simplifier::reduce1_app_core(app * n) { m_args.reset(); func_decl * decl = n->get_decl(); - proof_ref p1(m_manager); + proof_ref p1(m); // Stores the new arguments of n in m_args. // Let n be of the form // (decl arg_0 ... arg_{n-1}) @@ -296,23 +301,23 @@ void simplifier::reduce1_app_core(app * n) { // If none of the arguments have been simplified, and n is not a theory symbol, // Then no simplification is possible, and we can cache the result of the simplification of n as n. if (has_new_args || decl->get_family_id() != null_family_id) { - expr_ref r(m_manager); - TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m_manager);); + expr_ref r(m); + TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m);); // the method mk_app invokes get_subst and plugins to simplify // (decl arg_0' ... arg_{n-1}') mk_app(decl, m_args.size(), m_args.c_ptr(), r); - if (!m_manager.fine_grain_proofs()) { + if (!m.fine_grain_proofs()) { cache_result(n, r, 0); } else { - expr * s = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr()); + expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr()); proof * p; if (n == r) p = 0; else if (r != s) // we use a "theory rewrite generic proof" to justify the step // s = (decl arg_0' ... arg_{n-1}') --> r - p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(s, r)); + p = m.mk_transitivity(p1, m.mk_rewrite(s, r)); else p = p1; cache_result(n, r, p); @@ -354,11 +359,11 @@ bool is_ac_vector(app * n) { } void simplifier::reduce1_ac_app_core(app * n) { - app_ref n_c(m_manager); - proof_ref p1(m_manager); + app_ref n_c(m); + proof_ref p1(m); mk_ac_congruent_term(n, n_c, p1); - TRACE("ac", tout << "expr:\n" << mk_pp(n, m_manager) << "\ncongruent term:\n" << mk_pp(n_c, m_manager) << "\n";); - expr_ref r(m_manager); + TRACE("ac", tout << "expr:\n" << mk_pp(n, m) << "\ncongruent term:\n" << mk_pp(n_c, m) << "\n";); + expr_ref r(m); func_decl * decl = n->get_decl(); family_id fid = decl->get_family_id(); plugin * p = get_plugin(fid); @@ -376,7 +381,7 @@ void simplifier::reduce1_ac_app_core(app * n) { // done... } else { - r = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr()); + r = m.mk_app(decl, m_args.size(), m_args.c_ptr()); } } else { @@ -385,7 +390,7 @@ void simplifier::reduce1_ac_app_core(app * n) { get_ac_args(n_c, m_args, m_mults); TRACE("ac", tout << "AC args:\n"; for (unsigned i = 0; i < m_args.size(); i++) { - tout << mk_pp(m_args[i], m_manager) << " * " << m_mults[i] << "\n"; + tout << mk_pp(m_args[i], m) << " * " << m_mults[i] << "\n"; }); if (p != 0 && p->reduce(decl, m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), r)) { // done... @@ -393,12 +398,12 @@ void simplifier::reduce1_ac_app_core(app * n) { else { ptr_buffer new_args; expand_args(m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), new_args); - r = m_manager.mk_app(decl, new_args.size(), new_args.c_ptr()); + r = m.mk_app(decl, new_args.size(), new_args.c_ptr()); } } - TRACE("ac", tout << "AC result:\n" << mk_pp(r, m_manager) << "\n";); + TRACE("ac", tout << "AC result:\n" << mk_pp(r, m) << "\n";); - if (!m_manager.fine_grain_proofs()) { + if (!m.fine_grain_proofs()) { cache_result(n, r, 0); } else { @@ -406,7 +411,7 @@ void simplifier::reduce1_ac_app_core(app * n) { if (n == r.get()) p = 0; else if (r.get() != n_c.get()) - p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(n_c, r)); + p = m.mk_transitivity(p1, m.mk_rewrite(n_c, r)); else p = p1; cache_result(n, r, p); @@ -416,8 +421,8 @@ void simplifier::reduce1_ac_app_core(app * n) { static unsigned g_rewrite_lemma_id = 0; void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result) { - expr_ref arg(m_manager); - arg = m_manager.mk_app(decl, num_args, args); + expr_ref arg(m); + arg = m.mk_app(decl, num_args, args); if (arg.get() != result) { char buffer[128]; #ifdef _WINDOWS @@ -425,11 +430,11 @@ void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * #else sprintf(buffer, "rewrite_lemma_%d.smt", g_rewrite_lemma_id); #endif - ast_smt_pp pp(m_manager); + ast_smt_pp pp(m); pp.set_benchmark_name("rewrite_lemma"); pp.set_status("unsat"); - expr_ref n(m_manager); - n = m_manager.mk_not(m_manager.mk_eq(arg.get(), result)); + expr_ref n(m); + n = m.mk_not(m.mk_eq(arg.get(), result)); std::ofstream out(buffer); pp.display(out, n); out.close(); @@ -445,14 +450,14 @@ void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * */ void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result) { m_need_reset = true; - if (m_manager.is_eq(decl)) { - sort * s = m_manager.get_sort(args[0]); + if (m.is_eq(decl)) { + sort * s = m.get_sort(args[0]); plugin * p = get_plugin(s->get_family_id()); if (p != 0 && p->reduce_eq(args[0], args[1], result)) return; } - else if (m_manager.is_distinct(decl)) { - sort * s = m_manager.get_sort(args[0]); + else if (m.is_distinct(decl)) { + sort * s = m.get_sort(args[0]); plugin * p = get_plugin(s->get_family_id()); if (p != 0 && p->reduce_distinct(num_args, args, result)) return; @@ -464,7 +469,7 @@ void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args //dump_rewrite_lemma(decl, num_args, args, result.get()); return; } - result = m_manager.mk_app(decl, num_args, args); + result = m.mk_app(decl, num_args, args); } /** @@ -484,7 +489,7 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) { get_cached(arg, new_arg, arg_proof); CTRACE("simplifier_bug", (arg != new_arg) != (arg_proof != 0), - tout << mk_ll_pp(arg, m_manager) << "\n---->\n" << mk_ll_pp(new_arg, m_manager) << "\n"; + tout << mk_ll_pp(arg, m) << "\n---->\n" << mk_ll_pp(new_arg, m) << "\n"; tout << "#" << arg->get_id() << " #" << new_arg->get_id() << "\n"; tout << arg << " " << new_arg << "\n";); @@ -500,11 +505,11 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) { args.push_back(new_arg); } if (has_new_args) { - r = m_manager.mk_app(n->get_decl(), args.size(), args.c_ptr()); + r = m.mk_app(n->get_decl(), args.size(), args.c_ptr()); if (m_use_oeq) - p = m_manager.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr()); + p = m.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr()); else - p = m_manager.mk_congruence(n, r, proofs.size(), proofs.c_ptr()); + p = m.mk_congruence(n, r, proofs.size(), proofs.c_ptr()); } else { r = n; @@ -523,8 +528,8 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) { bool simplifier::get_args(app * n, ptr_vector & result, proof_ref & p) { bool has_new_args = false; unsigned num = n->get_num_args(); - if (m_manager.fine_grain_proofs()) { - app_ref r(m_manager); + if (m.fine_grain_proofs()) { + app_ref r(m); mk_congruent_term(n, r, p); result.append(r->get_num_args(), r->get_args()); SASSERT(n->get_num_args() == result.size()); @@ -582,7 +587,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { new_args.push_back(new_arg); if (arg != new_arg) has_new_arg = true; - if (m_manager.fine_grain_proofs()) { + if (m.fine_grain_proofs()) { proof * pr = 0; m_ac_pr_cache.find(to_app(arg), pr); if (pr != 0) @@ -601,7 +606,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { new_args.push_back(new_arg); if (arg != new_arg) has_new_arg = true; - if (m_manager.fine_grain_proofs() && pr != 0) + if (m.fine_grain_proofs() && pr != 0) new_arg_prs.push_back(pr); } } @@ -610,14 +615,14 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { todo.pop_back(); if (!has_new_arg) { m_ac_cache.insert(curr, curr); - if (m_manager.fine_grain_proofs()) + if (m.fine_grain_proofs()) m_ac_pr_cache.insert(curr, 0); } else { - app * new_curr = m_manager.mk_app(f, new_args.size(), new_args.c_ptr()); + app * new_curr = m.mk_app(f, new_args.size(), new_args.c_ptr()); m_ac_cache.insert(curr, new_curr); - if (m_manager.fine_grain_proofs()) { - proof * p = m_manager.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr()); + if (m.fine_grain_proofs()) { + proof * p = m.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr()); m_ac_pr_cache.insert(curr, p); } } @@ -628,7 +633,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { app * new_n = 0; m_ac_cache.find(n, new_n); r = new_n; - if (m_manager.fine_grain_proofs()) { + if (m.fine_grain_proofs()) { proof * new_pr = 0; m_ac_pr_cache.find(n, new_pr); p = new_pr; @@ -719,7 +724,7 @@ void simplifier::get_ac_args(app * n, ptr_vector & args, vector SASSERT(!sorted_exprs.empty()); SASSERT(sorted_exprs[sorted_exprs.size()-1] == n); - TRACE("ac", tout << mk_ll_pp(n, m_manager, true, false) << "#" << n->get_id() << "\nsorted expressions...\n"; + TRACE("ac", tout << mk_ll_pp(n, m, true, false) << "#" << n->get_id() << "\nsorted expressions...\n"; for (unsigned i = 0; i < sorted_exprs.size(); i++) { tout << "#" << sorted_exprs[i]->get_id() << " "; } @@ -754,10 +759,10 @@ void simplifier::get_ac_args(app * n, ptr_vector & args, vector void simplifier::reduce1_quantifier(quantifier * q) { expr * new_body; proof * new_body_pr; - SASSERT(is_well_sorted(m_manager, q)); + SASSERT(is_well_sorted(m, q)); get_cached(q->get_expr(), new_body, new_body_pr); - quantifier_ref q1(m_manager); + quantifier_ref q1(m); proof * p1 = 0; if (is_quantifier(new_body) && @@ -774,7 +779,7 @@ void simplifier::reduce1_quantifier(quantifier * q) { sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts()); names.append(nested_q->get_num_decls(), nested_q->get_decl_names()); - q1 = m_manager.mk_quantifier(q->is_forall(), + q1 = m.mk_quantifier(q->is_forall(), sorts.size(), sorts.c_ptr(), names.c_ptr(), @@ -783,13 +788,13 @@ void simplifier::reduce1_quantifier(quantifier * q) { q->get_qid(), q->get_skid(), 0, 0, 0, 0); - SASSERT(is_well_sorted(m_manager, q1)); + SASSERT(is_well_sorted(m, q1)); - if (m_manager.fine_grain_proofs()) { - quantifier * q0 = m_manager.update_quantifier(q, new_body); - proof * p0 = q == q0 ? 0 : m_manager.mk_quant_intro(q, q0, new_body_pr); - p1 = m_manager.mk_pull_quant(q0, q1); - p1 = m_manager.mk_transitivity(p0, p1); + if (m.fine_grain_proofs()) { + quantifier * q0 = m.update_quantifier(q, new_body); + proof * p0 = q == q0 ? 0 : m.mk_quant_intro(q, q0, new_body_pr); + p1 = m.mk_pull_quant(q0, q1); + p1 = m.mk_transitivity(p0, p1); } } else { @@ -802,7 +807,7 @@ void simplifier::reduce1_quantifier(quantifier * q) { unsigned num = q->get_num_patterns(); for (unsigned i = 0; i < num; i++) { get_cached(q->get_pattern(i), new_pattern, new_pattern_pr); - if (m_manager.is_pattern(new_pattern)) { + if (m.is_pattern(new_pattern)) { new_patterns.push_back(new_pattern); } } @@ -815,7 +820,7 @@ void simplifier::reduce1_quantifier(quantifier * q) { remove_duplicates(new_patterns); remove_duplicates(new_no_patterns); - q1 = m_manager.mk_quantifier(q->is_forall(), + q1 = m.mk_quantifier(q->is_forall(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), @@ -827,26 +832,26 @@ void simplifier::reduce1_quantifier(quantifier * q) { new_patterns.c_ptr(), new_no_patterns.size(), new_no_patterns.c_ptr()); - SASSERT(is_well_sorted(m_manager, q1)); + SASSERT(is_well_sorted(m, q1)); - TRACE("simplifier", tout << mk_pp(q, m_manager) << "\n" << mk_pp(q1, m_manager) << "\n";); - if (m_manager.fine_grain_proofs()) { + TRACE("simplifier", tout << mk_pp(q, m) << "\n" << mk_pp(q1, m) << "\n";); + if (m.fine_grain_proofs()) { if (q != q1 && !new_body_pr) { - new_body_pr = m_manager.mk_rewrite(q->get_expr(), new_body); + new_body_pr = m.mk_rewrite(q->get_expr(), new_body); } - p1 = q == q1 ? 0 : m_manager.mk_quant_intro(q, q1, new_body_pr); + p1 = q == q1 ? 0 : m.mk_quant_intro(q, q1, new_body_pr); } } - expr_ref r(m_manager); - elim_unused_vars(m_manager, q1, r); + expr_ref r(m); + elim_unused_vars(m, q1, r); proof * pr = 0; - if (m_manager.fine_grain_proofs()) { + if (m.fine_grain_proofs()) { proof * p2 = 0; if (q1.get() != r.get()) - p2 = m_manager.mk_elim_unused_vars(q1, r); - pr = m_manager.mk_transitivity(p1, p2); + p2 = m.mk_elim_unused_vars(q1, r); + pr = m.mk_transitivity(p1, p2); } cache_result(q, r, pr); @@ -892,7 +897,7 @@ bool subst_simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) { m_subst_map->get(n, _r, _p); r = _r; p = _p; - if (m_manager.coarse_grain_proofs()) + if (m.coarse_grain_proofs()) m_subst_proofs.push_back(p); return true; } diff --git a/src/ast/simplifier/simplifier.h b/src/ast/simplifier/simplifier.h index 7c5bc3102..899b25810 100644 --- a/src/ast/simplifier/simplifier.h +++ b/src/ast/simplifier/simplifier.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _SIMPLIFIER_H_ -#define _SIMPLIFIER_H_ +#ifndef SIMPLIFIER_H_ +#define SIMPLIFIER_H_ #include"base_simplifier.h" #include"simplifier_plugin.h" @@ -210,7 +210,7 @@ public: plugin * get_plugin(family_id fid) const { return m_plugins.get_plugin(fid); } - ast_manager & get_manager() { return m_manager; } + ast_manager & get_manager() { return m; } void borrow_plugins(simplifier const & s); void release_plugins(); diff --git a/src/ast/simplifier/simplifier_plugin.h b/src/ast/simplifier/simplifier_plugin.h index 8e176ea6e..b018dc731 100644 --- a/src/ast/simplifier/simplifier_plugin.h +++ b/src/ast/simplifier/simplifier_plugin.h @@ -15,8 +15,8 @@ Author: --*/ -#ifndef __SIMPLIFIER_PLUGIN_H__ -#define __SIMPLIFIER_PLUGIN_H__ +#ifndef SIMPLIFIER_PLUGIN_H_ +#define SIMPLIFIER_PLUGIN_H_ #include"ast.h" diff --git a/src/ast/static_features.h b/src/ast/static_features.h index 4bdcd6f05..8b20c5463 100644 --- a/src/ast/static_features.h +++ b/src/ast/static_features.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _STATIC_FEATURES_H_ -#define _STATIC_FEATURES_H_ +#ifndef STATIC_FEATURES_H_ +#define STATIC_FEATURES_H_ #include"ast.h" #include"arith_decl_plugin.h" @@ -142,6 +142,8 @@ struct static_features { } } + bool arith_k_sum_is_small() const { return m_arith_k_sum < rational(INT_MAX / 8); } + void inc_num_apps(func_decl const * d) { unsigned id = d->get_decl_id(); m_num_apps.reserve(id+1, 0); m_num_apps[id]++; } void inc_theory_terms(family_id fid) { m_num_theory_terms.reserve(fid+1, 0); m_num_theory_terms[fid]++; } void inc_theory_atoms(family_id fid) { m_num_theory_atoms.reserve(fid+1, 0); m_num_theory_atoms[fid]++; } @@ -176,5 +178,5 @@ struct static_features { }; -#endif /* _STATIC_FEATURES_H_ */ +#endif /* STATIC_FEATURES_H_ */ diff --git a/src/ast/substitution/expr_offset.h b/src/ast/substitution/expr_offset.h index 854720c9e..ea4f36206 100644 --- a/src/ast/substitution/expr_offset.h +++ b/src/ast/substitution/expr_offset.h @@ -21,8 +21,8 @@ Author: Revision History: --*/ -#ifndef _EXPR_OFFSET_H_ -#define _EXPR_OFFSET_H_ +#ifndef EXPR_OFFSET_H_ +#define EXPR_OFFSET_H_ #include"ast.h" @@ -50,4 +50,4 @@ public: typedef std::pair expr_offset_pair; typedef pair_hash, obj_hash > expr_offset_pair_hash; -#endif /* _EXPR_OFFSET_H_ */ +#endif /* EXPR_OFFSET_H_ */ diff --git a/src/ast/substitution/expr_offset_map.h b/src/ast/substitution/expr_offset_map.h index dac5ceac6..83e22493b 100644 --- a/src/ast/substitution/expr_offset_map.h +++ b/src/ast/substitution/expr_offset_map.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _EXPR_OFFSET_MAP_H_ -#define _EXPR_OFFSET_MAP_H_ +#ifndef EXPR_OFFSET_MAP_H_ +#define EXPR_OFFSET_MAP_H_ #include"expr_offset.h" #include"vector.h" @@ -91,4 +91,4 @@ public: } }; -#endif /* _EXPR_OFFSET_MAP_H_ */ +#endif /* EXPR_OFFSET_MAP_H_ */ diff --git a/src/ast/substitution/matcher.h b/src/ast/substitution/matcher.h index 84a62e874..c4936579e 100644 --- a/src/ast/substitution/matcher.h +++ b/src/ast/substitution/matcher.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MATCHER_H_ -#define _MATCHER_H_ +#ifndef MATCHER_H_ +#define MATCHER_H_ #include"substitution.h" #include"hashtable.h" @@ -59,5 +59,5 @@ public: bool operator()(expr * e1, expr * e2, substitution & s); }; -#endif /* _MATCHER_H_ */ +#endif /* MATCHER_H_ */ diff --git a/src/ast/substitution/substitution.h b/src/ast/substitution/substitution.h index 3227a6fec..ebd12ef3a 100644 --- a/src/ast/substitution/substitution.h +++ b/src/ast/substitution/substitution.h @@ -30,8 +30,8 @@ Revision History: of the current substitution by the number of quantified variables. --*/ -#ifndef _SUBSTITUTION_H_ -#define _SUBSTITUTION_H_ +#ifndef SUBSTITUTION_H_ +#define SUBSTITUTION_H_ #include"expr_offset_map.h" #include"var_offset_map.h" diff --git a/src/ast/substitution/substitution_tree.cpp b/src/ast/substitution/substitution_tree.cpp index 037d51e32..6aaa2da66 100644 --- a/src/ast/substitution/substitution_tree.cpp +++ b/src/ast/substitution/substitution_tree.cpp @@ -793,8 +793,10 @@ bool substitution_tree::visit(expr * e, st_visitor & st, node * r) { } else { TRACE("st_bug", tout << "found match:\n"; m_subst->display(tout); tout << "m_subst: " << m_subst << "\n";); - if (!st(n->m_expr)) + if (!st(n->m_expr)) { + clear_stack(); return false; + } if (!backtrack()) break; } @@ -806,12 +808,16 @@ bool substitution_tree::visit(expr * e, st_visitor & st, node * r) { else if (!backtrack()) break; } + clear_stack(); + return true; +} + +void substitution_tree::clear_stack() { while (!m_bstack.empty()) { m_subst->pop_scope(); m_bstack.pop_back(); } m_subst->pop_scope(); - return true; } template diff --git a/src/ast/substitution/substitution_tree.h b/src/ast/substitution/substitution_tree.h index caa3d37cb..167d08183 100644 --- a/src/ast/substitution/substitution_tree.h +++ b/src/ast/substitution/substitution_tree.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SUBSTITUTION_TREE_H_ -#define _SUBSTITUTION_TREE_H_ +#ifndef SUBSTITUTION_TREE_H_ +#define SUBSTITUTION_TREE_H_ #include"ast.h" #include"substitution.h" @@ -123,6 +123,8 @@ class substitution_tree { template void visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset); + void clear_stack(); + public: substitution_tree(ast_manager & m); ~substitution_tree(); @@ -144,5 +146,5 @@ public: void display(std::ostream & out) const; }; -#endif /* _SUBSTITUTION_TREE_H_ */ +#endif /* SUBSTITUTION_TREE_H_ */ diff --git a/src/ast/substitution/unifier.h b/src/ast/substitution/unifier.h index 315cea092..24735117c 100644 --- a/src/ast/substitution/unifier.h +++ b/src/ast/substitution/unifier.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _UNIFIER_H_ -#define _UNIFIER_H_ +#ifndef UNIFIER_H_ +#define UNIFIER_H_ #include"ast.h" #include"substitution.h" @@ -66,5 +66,5 @@ public: bool operator()(expr * e1, expr * e2, substitution & s, bool use_offsets = true); }; -#endif /* _UNIFIER_H_ */ +#endif /* UNIFIER_H_ */ diff --git a/src/ast/substitution/var_offset_map.h b/src/ast/substitution/var_offset_map.h index 85120e614..f03b80aed 100644 --- a/src/ast/substitution/var_offset_map.h +++ b/src/ast/substitution/var_offset_map.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _VAR_OFFSET_MAP_H_ -#define _VAR_OFFSET_MAP_H_ +#ifndef VAR_OFFSET_MAP_H_ +#define VAR_OFFSET_MAP_H_ #include"ast.h" #include"vector.h" @@ -111,4 +111,4 @@ public: }; -#endif /* _VAR_OFFSET_MAP_H_ */ +#endif /* VAR_OFFSET_MAP_H_ */ diff --git a/src/ast/used_symbols.h b/src/ast/used_symbols.h index 81086d753..994e3321a 100644 --- a/src/ast/used_symbols.h +++ b/src/ast/used_symbols.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _USED_SYMBOLS_H_ -#define _USED_SYMBOLS_H_ +#ifndef USED_SYMBOLS_H_ +#define USED_SYMBOLS_H_ #include"ast.h" #include"hashtable.h" @@ -102,4 +102,4 @@ public: void erase_core(symbol const & s) { m_used.erase(s); } }; -#endif /* _USED_SYMBOLS_H_ */ +#endif /* USED_SYMBOLS_H_ */ diff --git a/src/ast/used_vars.h b/src/ast/used_vars.h index 142ce9bcd..b14798e7d 100644 --- a/src/ast/used_vars.h +++ b/src/ast/used_vars.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _USED_VARS_H_ -#define _USED_VARS_H_ +#ifndef USED_VARS_H_ +#define USED_VARS_H_ #include"ast.h" #include"expr_delta_pair.h" @@ -55,5 +55,5 @@ public: unsigned get_num_vars() const; }; -#endif /* _USED_VARS_H_ */ +#endif /* USED_VARS_H_ */ diff --git a/src/ast/well_sorted.h b/src/ast/well_sorted.h index 488e6b02a..b2eb4dfd4 100644 --- a/src/ast/well_sorted.h +++ b/src/ast/well_sorted.h @@ -16,13 +16,13 @@ Author: Revision History: --*/ -#ifndef _WELL_SORTED_H_ -#define _WELL_SORTED_H_ +#ifndef WELL_SORTED_H_ +#define WELL_SORTED_H_ class ast_manager; class expr; bool is_well_sorted(ast_manager const & m, expr * n); -#endif /* _WELL_SORTED_H_ */ +#endif /* WELL_SORTED_H_ */ diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 94bd4f5e1..aa2b7b289 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -21,14 +21,12 @@ Notes: #include"ast_smt2_pp.h" #include"ast_pp.h" #include"model_smt2_pp.h" -#include"model_v2_pp.h" #include"array_decl_plugin.h" #include"pp.h" #include"cmd_util.h" #include"simplify_cmd.h" #include"eval_cmd.h" #include"gparams.h" -#include"model_params.hpp" #include"env_params.h" class help_cmd : public cmd { @@ -105,17 +103,7 @@ ATOMIC_CMD(get_model_cmd, "get-model", "retrieve model for the last check-sat co throw cmd_exception("model is not available"); model_ref m; ctx.get_check_sat_result()->get_model(m); - model_params p; - if (p.v1() || p.v2()) { - std::ostringstream buffer; - model_v2_pp(buffer, *m, p.partial()); - ctx.regular_stream() << "\"" << escaped(buffer.str().c_str(), true) << "\"" << std::endl; - } else { - ctx.regular_stream() << "(model " << std::endl; - model_smt2_pp(ctx.regular_stream(), ctx, *(m.get()), 2); - // m->display(ctx.regular_stream()); - ctx.regular_stream() << ")" << std::endl; - } + ctx.display_model(m); }); ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { diff --git a/src/cmd_context/basic_cmds.h b/src/cmd_context/basic_cmds.h index 554c1ab31..3e896a024 100644 --- a/src/cmd_context/basic_cmds.h +++ b/src/cmd_context/basic_cmds.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _BASIC_CMDS_H_ -#define _BASIC_CMDS_H_ +#ifndef BASIC_CMDS_H_ +#define BASIC_CMDS_H_ class cmd_context; diff --git a/src/cmd_context/check_logic.h b/src/cmd_context/check_logic.h index 6fdfc5654..888c6ed9f 100644 --- a/src/cmd_context/check_logic.h +++ b/src/cmd_context/check_logic.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _CHECK_LOGIC_H_ -#define _CHECK_LOGIC_H_ +#ifndef CHECK_LOGIC_H_ +#define CHECK_LOGIC_H_ #include"ast.h" diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 0c60d876b..8648775ee 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -24,6 +24,7 @@ Notes: #include"array_decl_plugin.h" #include"datatype_decl_plugin.h" #include"seq_decl_plugin.h" +#include"pb_decl_plugin.h" #include"fpa_decl_plugin.h" #include"ast_pp.h" #include"var_subst.h" @@ -39,6 +40,9 @@ Notes: #include"for_each_expr.h" #include"scoped_timer.h" #include"interpolant_cmds.h" +#include"model_smt2_pp.h" +#include"model_v2_pp.h" +#include"model_params.hpp" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { @@ -336,10 +340,10 @@ cmd_context::~cmd_context() { if (m_main_ctx) { set_verbose_stream(std::cerr); } - reset(true); finalize_cmds(); finalize_tactic_cmds(); finalize_probes(); + reset(true); m_solver = 0; m_check_sat_result = 0; } @@ -357,6 +361,18 @@ void cmd_context::set_cancel(bool f) { m().set_cancel(f); } +opt_wrapper* cmd_context::get_opt() { + return m_opt.get(); +} + +void cmd_context::set_opt(opt_wrapper* opt) { + m_opt = opt; + for (unsigned i = 0; i < m_scopes.size(); ++i) { + m_opt->push(); + } + m_opt->set_logic(m_logic); +} + void cmd_context::global_params_updated() { m_params.updt_params(); if (m_params.m_smtlib2_compliant) @@ -487,6 +503,7 @@ bool cmd_context::logic_has_arith_core(symbol const & s) const { s == "QF_RDL" || s == "QF_IDL" || s == "QF_AUFLIA" || + s == "QF_ALIA" || s == "QF_AUFLIRA" || s == "QF_AUFNIA" || s == "QF_AUFNIRA" || @@ -514,6 +531,7 @@ bool cmd_context::logic_has_arith_core(symbol const & s) const { s == "LRA" || s == "QF_FP" || s == "QF_FPBV" || + s == "QF_BVFP" || s == "HORN"; } @@ -533,13 +551,12 @@ bool cmd_context::logic_has_bv_core(symbol const & s) const { s == "QF_AUFBV" || s == "QF_BVRE" || s == "QF_FPBV" || + s == "QF_BVFP" || s == "HORN"; } bool cmd_context::logic_has_horn(symbol const& s) const { - return - s == "HORN"; - + return s == "HORN"; } bool cmd_context::logic_has_bv() const { @@ -547,23 +564,26 @@ bool cmd_context::logic_has_bv() const { } bool cmd_context::logic_has_seq_core(symbol const& s) const { - return - s == "QF_BVRE"; - + return s == "QF_BVRE"; } bool cmd_context::logic_has_seq() const { return !has_logic() || logic_has_seq_core(m_logic); } +bool cmd_context::logic_has_fpa_core(symbol const& s) const { + return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP"; +} + bool cmd_context::logic_has_fpa() const { - return !has_logic() || m_logic == "QF_FP" || m_logic == "QF_FPBV"; + return !has_logic() || logic_has_fpa_core(m_logic); } bool cmd_context::logic_has_array_core(symbol const & s) const { return s == "QF_AX" || s == "QF_AUFLIA" || + s == "QF_ALIA" || s == "QF_AUFLIRA" || s == "QF_AUFNIA" || s == "QF_AUFNIRA" || @@ -601,7 +621,9 @@ void cmd_context::init_manager_core(bool new_manager) { register_plugin(symbol("array"), alloc(array_decl_plugin), logic_has_array()); register_plugin(symbol("datatype"), alloc(datatype_decl_plugin), logic_has_datatype()); register_plugin(symbol("seq"), alloc(seq_decl_plugin), logic_has_seq()); + register_plugin(symbol("pb"), alloc(pb_decl_plugin), !has_logic()); register_plugin(symbol("fpa"), alloc(fpa_decl_plugin), logic_has_fpa()); + register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin), !has_logic()); } else { // the manager was created by an external module @@ -667,8 +689,7 @@ bool cmd_context::supported_logic(symbol const & s) const { return s == "QF_UF" || s == "UF" || logic_has_arith_core(s) || logic_has_bv_core(s) || logic_has_array_core(s) || logic_has_seq_core(s) || - logic_has_horn(s) || - s == "QF_FP" || s == "QF_FPBV"; + logic_has_horn(s) || logic_has_fpa_core(s); } bool cmd_context::set_logic(symbol const & s) { @@ -736,7 +757,7 @@ void cmd_context::insert(symbol const & s, func_decl * f) { if (!m_global_decls) { m_func_decls_stack.push_back(sf_pair(s, f)); } - TRACE("cmd_context", tout << "new sort decl\n" << mk_pp(f, m()) << "\n";); + TRACE("cmd_context", tout << "new function decl\n" << mk_pp(f, m()) << "\n";); } void cmd_context::insert(symbol const & s, psort_decl * p) { @@ -1169,7 +1190,6 @@ void cmd_context::insert_aux_pdecl(pdecl * p) { } void cmd_context::reset(bool finalize) { - m_check_sat_result = 0; m_logic = symbol::null; m_check_sat_result = 0; m_numeral_as_real = false; @@ -1186,6 +1206,7 @@ void cmd_context::reset(bool finalize) { if (m_solver) m_solver = 0; m_scopes.reset(); + m_opt = 0; m_pp_env = 0; m_dt_eh = 0; if (m_manager) { @@ -1253,6 +1274,8 @@ void cmd_context::push() { s.m_assertions_lim = m_assertions.size(); if (m_solver) m_solver->push(); + if (m_opt) + m_opt->push(); } void cmd_context::push(unsigned n) { @@ -1348,6 +1371,8 @@ void cmd_context::pop(unsigned n) { if (m_solver) { m_solver->pop(n); } + if (m_opt) + m_opt->pop(n); unsigned new_lvl = lvl - n; scope & s = m_scopes[new_lvl]; restore_func_decls(s.m_func_decls_stack_lim); @@ -1364,15 +1389,56 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions IF_VERBOSE(100, verbose_stream() << "(started \"check-sat\")" << std::endl;); TRACE("before_check_sat", dump_assertions(tout);); init_manager(); - if (m_solver) { + unsigned timeout = m_params.m_timeout; + unsigned rlimit = m_params.m_rlimit; + scoped_watch sw(*this); + lbool r; + + if (m_opt && !m_opt->empty()) { + bool was_pareto = false; + m_check_sat_result = get_opt(); + cancel_eh eh(*get_opt()); + scoped_ctrl_c ctrlc(eh); + scoped_timer timer(timeout, &eh); + scoped_rlimit _rlimit(m().limit(), rlimit); + ptr_vector cnstr(m_assertions); + cnstr.append(num_assumptions, assumptions); + get_opt()->set_hard_constraints(cnstr); + try { + r = get_opt()->optimize(); + while (r == l_true && get_opt()->is_pareto()) { + was_pareto = true; + get_opt()->display_assignment(regular_stream()); + regular_stream() << "\n"; + if (get_opt()->print_model()) { + model_ref mdl; + get_opt()->get_model(mdl); + display_model(mdl); + } + r = get_opt()->optimize(); + } + } + catch (z3_error & ex) { + throw ex; + } + catch (z3_exception & ex) { + throw cmd_exception(ex.msg()); + } + if (was_pareto && r == l_false) { + r = l_true; + } + get_opt()->set_status(r); + if (r != l_false && !was_pareto) { + get_opt()->display_assignment(regular_stream()); + } + } + else if (m_solver) { m_check_sat_result = m_solver.get(); // solver itself stores the result. m_solver->set_progress_callback(this); - unsigned timeout = m_params.m_timeout; - scoped_watch sw(*this); cancel_eh eh(*m_solver); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); - lbool r; + scoped_rlimit _rlimit(m().limit(), rlimit); try { r = m_solver->check_sat(num_assumptions, assumptions); } @@ -1383,14 +1449,36 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions throw cmd_exception(ex.msg()); } m_solver->set_status(r); - display_sat_result(r); - validate_check_sat_result(r); - if (r == l_true) - validate_model(); } else { // There is no solver installed in the command context. regular_stream() << "unknown" << std::endl; + return; + } + display_sat_result(r); + validate_check_sat_result(r); + if (r == l_true) { + validate_model(); + if (m_params.m_dump_models) { + model_ref md; + get_check_sat_result()->get_model(md); + display_model(md); + } + } +} + +void cmd_context::display_model(model_ref& mdl) { + if (mdl) { + model_params p; + if (p.v1() || p.v2()) { + std::ostringstream buffer; + model_v2_pp(buffer, *mdl, p.partial()); + regular_stream() << "\"" << escaped(buffer.str().c_str(), true) << "\"" << std::endl; + } else { + regular_stream() << "(model " << std::endl; + model_smt2_pp(regular_stream(), *this, *mdl, 2); + regular_stream() << ")" << std::endl; + } } } @@ -1557,17 +1645,20 @@ void cmd_context::set_solver_factory(solver_factory * f) { void cmd_context::display_statistics(bool show_total_time, double total_time) { statistics st; - unsigned long long mem = memory::get_max_used_memory(); if (show_total_time) st.update("total time", total_time); st.update("time", get_seconds()); - st.update("memory", static_cast(mem)/static_cast(1024*1024)); + get_memory_statistics(st); + get_rlimit_statistics(m().limit(), st); if (m_check_sat_result) { m_check_sat_result->collect_statistics(st); } else if (m_solver) { m_solver->collect_statistics(st); } + else if (m_opt) { + m_opt->collect_statistics(st); + } st.display_smt2(regular_stream()); } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index f9e50e611..86563eb9b 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -18,8 +18,8 @@ Author: Notes: --*/ -#ifndef _CMD_CONTEXT_H_ -#define _CMD_CONTEXT_H_ +#ifndef CMD_CONTEXT_H_ +#define CMD_CONTEXT_H_ #include #include"ast.h" @@ -111,6 +111,22 @@ struct builtin_decl { builtin_decl(family_id fid, decl_kind k, builtin_decl * n = 0):m_fid(fid), m_decl(k), m_next(n) {} }; +class opt_wrapper : public check_sat_result { +public: + virtual bool empty() = 0; + virtual void push() = 0; + virtual void pop(unsigned n) = 0; + virtual void set_cancel(bool f) = 0; + virtual void reset_cancel() = 0; + virtual void cancel() = 0; + virtual lbool optimize() = 0; + virtual void set_hard_constraints(ptr_vector & hard) = 0; + virtual void display_assignment(std::ostream& out) = 0; + virtual bool is_pareto() = 0; + virtual void set_logic(symbol const& s) = 0; + virtual bool print_model() const = 0; +}; + class cmd_context : public progress_callback, public tactic_manager, public ast_printer_context { public: enum status { @@ -188,8 +204,9 @@ protected: svector m_scopes; scoped_ptr m_solver_factory; scoped_ptr m_interpolating_solver_factory; - ref m_solver; + ref m_solver; ref m_check_sat_result; + ref m_opt; stopwatch m_watch; @@ -213,7 +230,6 @@ protected: void register_builtin_sorts(decl_plugin * p); void register_builtin_ops(decl_plugin * p); - void register_plugin(symbol const & name, decl_plugin * p, bool install_names); void load_plugin(symbol const & name, bool install_names, svector& fids); void init_manager_core(bool new_manager); void init_manager(); @@ -235,6 +251,7 @@ protected: bool logic_has_bv_core(symbol const & s) const; bool logic_has_array_core(symbol const & s) const; bool logic_has_seq_core(symbol const & s) const; + bool logic_has_fpa_core(symbol const & s) const; bool logic_has_horn(symbol const& s) const; bool logic_has_arith() const; bool logic_has_bv() const; @@ -258,6 +275,8 @@ public: context_params & params() { return m_params; } solver_factory &get_solver_factory() { return *m_solver_factory; } solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; } + opt_wrapper* get_opt(); + void set_opt(opt_wrapper* o); void global_params_updated(); // this method should be invoked when global (and module) params are updated. bool set_logic(symbol const & s); bool has_logic() const { return m_logic != symbol::null; } @@ -306,7 +325,9 @@ public: check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); } check_sat_state cs_state() const; void validate_model(); - + void display_model(model_ref& mdl); + + void register_plugin(symbol const & name, decl_plugin * p, bool install_names); bool is_func_decl(symbol const & s) const; bool is_sort_decl(symbol const& s) const { return m_psort_decls.contains(s); } void insert(cmd * c); diff --git a/src/cmd_context/cmd_context_to_goal.h b/src/cmd_context/cmd_context_to_goal.h index 2bc27baa7..35e11e6c1 100644 --- a/src/cmd_context/cmd_context_to_goal.h +++ b/src/cmd_context/cmd_context_to_goal.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _CMD_CONTEXT_TO_GOAL_H_ -#define _CMD_CONTEXT_TO_GOAL_H_ +#ifndef CMD_CONTEXT_TO_GOAL_H_ +#define CMD_CONTEXT_TO_GOAL_H_ void assert_exprs_from(cmd_context const & ctx, goal & t); diff --git a/src/cmd_context/cmd_util.h b/src/cmd_context/cmd_util.h index 538964ad9..f0660af37 100644 --- a/src/cmd_context/cmd_util.h +++ b/src/cmd_context/cmd_util.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _CMD_UTIL_H_ -#define _CMD_UTIL_H_ +#ifndef CMD_UTIL_H_ +#define CMD_UTIL_H_ #define ATOMIC_CMD(CLS_NAME, NAME, DESCR, ACTION) \ class CLS_NAME : public cmd { \ diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index 13f6f5be3..add1b5b3d 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -27,6 +27,7 @@ context_params::context_params() { m_unsat_core = false; m_model = true; m_model_validate = false; + m_dump_models = false; m_auto_config = true; m_proof = false; m_trace = false; @@ -34,6 +35,7 @@ context_params::context_params() { m_smtlib2_compliant = false; m_well_sorted_check = false; m_timeout = UINT_MAX; + m_rlimit = UINT_MAX; updt_params(); } @@ -45,7 +47,9 @@ void context_params::set_bool(bool & opt, char const * param, char const * value opt = false; } else { - throw default_exception("invalid value '%s' for Boolean parameter '%s'", value, param); + std::stringstream strm; + strm << "invalid value '" << value << "' for Boolean parameter '" << param; + throw default_exception(strm.str()); } } @@ -62,6 +66,10 @@ void context_params::set(char const * param, char const * value) { long val = strtol(value, 0, 10); m_timeout = static_cast(val); } + if (p == "rlimit") { + long val = strtol(value, 0, 10); + m_rlimit = static_cast(val); + } else if (p == "type_check" || p == "well_sorted_check") { set_bool(m_well_sorted_check, param, value); } @@ -77,6 +85,9 @@ void context_params::set(char const * param, char const * value) { else if (p == "model_validate") { set_bool(m_model_validate, param, value); } + else if (p == "dump_models") { + set_bool(m_dump_models, param, value); + } else if (p == "trace") { set_bool(m_trace, param, value); } @@ -93,13 +104,13 @@ void context_params::set(char const * param, char const * value) { set_bool(m_smtlib2_compliant, param, value); } else { - param_descrs d; - collect_param_descrs(d); - std::stringstream strm; - strm << "unknown parameter '" << p << "'\n"; - strm << "Legal parameters are:\n"; - d.display(strm, 2, false, false); - throw default_exception(strm.str()); + param_descrs d; + collect_param_descrs(d); + std::stringstream strm; + strm << "unknown parameter '" << p << "'\n"; + strm << "Legal parameters are:\n"; + d.display(strm, 2, false, false); + throw default_exception(strm.str()); } } @@ -109,11 +120,13 @@ void context_params::updt_params() { void context_params::updt_params(params_ref const & p) { m_timeout = p.get_uint("timeout", m_timeout); + m_rlimit = p.get_uint("rlimit", m_rlimit); m_well_sorted_check = p.get_bool("type_check", p.get_bool("well_sorted_check", m_well_sorted_check)); m_auto_config = p.get_bool("auto_config", m_auto_config); m_proof = p.get_bool("proof", m_proof); m_model = p.get_bool("model", m_model); m_model_validate = p.get_bool("model_validate", m_model_validate); + m_dump_models = p.get_bool("dump_models", m_dump_models); m_trace = p.get_bool("trace", m_trace); m_trace_file_name = p.get_str("trace_file_name", "z3.log"); m_unsat_core = p.get_bool("unsat_core", m_unsat_core); @@ -123,10 +136,12 @@ void context_params::updt_params(params_ref const & p) { void context_params::collect_param_descrs(param_descrs & d) { d.insert("timeout", CPK_UINT, "default timeout (in milliseconds) used for solvers", "4294967295"); - d.insert("well_sorted_check", CPK_BOOL, "type checker", "true"); + d.insert("rlimit", CPK_UINT, "default resource limit used for solvers", "4294967295"); + d.insert("well_sorted_check", CPK_BOOL, "type checker", "false"); d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true"); d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true"); d.insert("model_validate", CPK_BOOL, "validate models produced by solvers", "false"); + d.insert("dump_models", CPK_BOOL, "dump models whenever check-sat returns sat", "false"); d.insert("trace", CPK_BOOL, "trace generation for VCC", "false"); d.insert("trace_file_name", CPK_STRING, "trace out file name (see option 'trace')", "z3.log"); d.insert("debug_ref_count", CPK_BOOL, "debug support for AST reference counting", "false"); diff --git a/src/cmd_context/context_params.h b/src/cmd_context/context_params.h index 506e559db..2c75b5743 100644 --- a/src/cmd_context/context_params.h +++ b/src/cmd_context/context_params.h @@ -17,8 +17,8 @@ Author: Notes: --*/ -#ifndef _CONTEXT_PARAMS_H_ -#define _CONTEXT_PARAMS_H_ +#ifndef CONTEXT_PARAMS_H_ +#define CONTEXT_PARAMS_H_ #include"params.h" class ast_manager; @@ -36,9 +36,11 @@ public: bool m_well_sorted_check; bool m_model; bool m_model_validate; + bool m_dump_models; bool m_unsat_core; bool m_smtlib2_compliant; // it must be here because it enable/disable the use of coercions in the ast_manager. unsigned m_timeout; + unsigned m_rlimit; context_params(); void set(char const * param, char const * value); diff --git a/src/cmd_context/echo_tactic.h b/src/cmd_context/echo_tactic.h index 8760820bd..2ba97ee47 100644 --- a/src/cmd_context/echo_tactic.h +++ b/src/cmd_context/echo_tactic.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _ECHO_TACTICS_H_ -#define _ECHO_TACTICS_H_ +#ifndef ECHO_TACTICS_H_ +#define ECHO_TACTICS_H_ class cmd_context; class tactic; diff --git a/src/cmd_context/eval_cmd.h b/src/cmd_context/eval_cmd.h index e6e220b1d..c71e57f69 100644 --- a/src/cmd_context/eval_cmd.h +++ b/src/cmd_context/eval_cmd.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _EVAL_CMD_H_ -#define _EVAL_CMD_H_ +#ifndef EVAL_CMD_H_ +#define EVAL_CMD_H_ class cmd_context; diff --git a/src/cmd_context/extra_cmds/dbg_cmds.h b/src/cmd_context/extra_cmds/dbg_cmds.h index 8a992a4b8..2ff73a11f 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.h +++ b/src/cmd_context/extra_cmds/dbg_cmds.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _DBG_CMDS_H_ -#define _DBG_CMDS_H_ +#ifndef DBG_CMDS_H_ +#define DBG_CMDS_H_ class cmd_context; diff --git a/src/cmd_context/extra_cmds/polynomial_cmds.h b/src/cmd_context/extra_cmds/polynomial_cmds.h index b2b368833..f1f80ae5c 100644 --- a/src/cmd_context/extra_cmds/polynomial_cmds.h +++ b/src/cmd_context/extra_cmds/polynomial_cmds.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _POLYNOMIAL_CMDS_H_ -#define _POLYNOMIAL_CMDS_H_ +#ifndef POLYNOMIAL_CMDS_H_ +#define POLYNOMIAL_CMDS_H_ class cmd_context; void install_polynomial_cmds(cmd_context & ctx); diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index f41f175ea..c482b4d09 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -36,11 +36,11 @@ #include"scoped_proof.h" static void show_interpolant_and_maybe_check(cmd_context & ctx, - ptr_vector &cnsts, - expr *t, - ptr_vector &interps, - params_ref &m_params, - bool check) + ptr_vector &cnsts, + expr *t, + ptr_vector &interps, + params_ref &m_params, + bool check) { if (m_params.get_bool("som", false)) @@ -206,17 +206,13 @@ static expr *make_tree(cmd_context & ctx, const ptr_vector &exprs){ } static void get_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { - expr *foo = make_tree(ctx,exprs); - ctx.m().inc_ref(foo); - get_interpolant(ctx,foo,m_params); - ctx.m().dec_ref(foo); + expr_ref foo(make_tree(ctx, exprs),ctx.m()); + get_interpolant(ctx,foo.get(),m_params); } static void compute_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { - expr *foo = make_tree(ctx, exprs); - ctx.m().inc_ref(foo); - compute_interpolant_and_maybe_check(ctx,foo,m_params,false); - ctx.m().dec_ref(foo); + expr_ref foo(make_tree(ctx, exprs),ctx.m()); + compute_interpolant_and_maybe_check(ctx,foo.get(),m_params,false); } diff --git a/src/cmd_context/interpolant_cmds.h b/src/cmd_context/interpolant_cmds.h index a7c127a75..daef70926 100644 --- a/src/cmd_context/interpolant_cmds.h +++ b/src/cmd_context/interpolant_cmds.h @@ -15,8 +15,8 @@ Notes: --*/ -#ifndef _INTERPOLANT_CMDS_H_ -#define _INTERPOLANT_CMDS_H_ +#ifndef INTERPOLANT_CMDS_H_ +#define INTERPOLANT_CMDS_H_ class cmd_context; void install_interpolant_cmds(cmd_context & ctx); diff --git a/src/cmd_context/parametric_cmd.h b/src/cmd_context/parametric_cmd.h index 762d6bd72..3e95832c4 100644 --- a/src/cmd_context/parametric_cmd.h +++ b/src/cmd_context/parametric_cmd.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _PARAMETRIC_CMD_H_ -#define _PARAMETRIC_CMD_H_ +#ifndef PARAMETRIC_CMD_H_ +#define PARAMETRIC_CMD_H_ #include"params.h" #include"symbol.h" diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h index 83d881f57..f03d1b901 100644 --- a/src/cmd_context/pdecl.h +++ b/src/cmd_context/pdecl.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _PDECL_H_ -#define _PDECL_H_ +#ifndef PDECL_H_ +#define PDECL_H_ #include"ast.h" #include"obj_hashtable.h" diff --git a/src/cmd_context/simplify_cmd.h b/src/cmd_context/simplify_cmd.h index 1d77e5e89..a0a06e89c 100644 --- a/src/cmd_context/simplify_cmd.h +++ b/src/cmd_context/simplify_cmd.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _SIMPLIFY_CMD_H_ -#define _SIMPLIFY_CMD_H_ +#ifndef SIMPLIFY_CMD_H_ +#define SIMPLIFY_CMD_H_ class cmd_context; diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index 75d56928e..ab2ad3922 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -157,11 +157,9 @@ public: void display_statistics(cmd_context & ctx, tactic * t) { statistics stats; - unsigned long long max_mem = memory::get_max_used_memory(); - unsigned long long mem = memory::get_allocation_size(); + get_memory_statistics(stats); + get_rlimit_statistics(ctx.m().limit(), stats); stats.update("time", ctx.get_seconds()); - stats.update("memory", static_cast(mem)/static_cast(1024*1024)); - stats.update("max memory", static_cast(max_mem)/static_cast(1024*1024)); t->collect_statistics(stats); stats.display_smt2(ctx.regular_stream()); } diff --git a/src/cmd_context/tactic_cmds.h b/src/cmd_context/tactic_cmds.h index c44078a90..e21d818bb 100644 --- a/src/cmd_context/tactic_cmds.h +++ b/src/cmd_context/tactic_cmds.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _TACTIC_CMDS_H_ -#define _TACTIC_CMDS_H_ +#ifndef TACTIC_CMDS_H_ +#define TACTIC_CMDS_H_ #include"ast.h" #include"cmd_context_types.h" diff --git a/src/cmd_context/tactic_manager.h b/src/cmd_context/tactic_manager.h index bcc24e090..44a25f01e 100644 --- a/src/cmd_context/tactic_manager.h +++ b/src/cmd_context/tactic_manager.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _TACTIC_MANAGER_H_ -#define _TACTIC_MANAGER_H_ +#ifndef TACTIC_MANAGER_H_ +#define TACTIC_MANAGER_H_ #include"tactic_cmds.h" #include"dictionary.h" diff --git a/src/duality/duality.h b/src/duality/duality.h old mode 100755 new mode 100644 diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 0a3692295..897bc521f 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -1171,7 +1171,7 @@ namespace Duality { new_alits.push_back(conj); #endif slvr().add(ctx.make(Implies, res, conj)); - // std::cout << res << ": " << conj << "\n"; + // std::cout << res << ": " << conj << "\n"; } if (opt_map) (*opt_map)[res] = conj; @@ -2734,7 +2734,7 @@ namespace Duality { if(res != unsat) throw "should be unsat"; s.pop(1); - + for(unsigned i = 0; i < conjuncts.size(); ){ std::swap(conjuncts[i],conjuncts.back()); expr save = conjuncts.back(); @@ -3056,7 +3056,7 @@ namespace Duality { Push(); expr the_impl = is.get_implicant(); if(eq(the_impl,prev_impl)){ - // std::cout << "got old implicant\n"; + // std::cout << "got old implicant\n"; repeated_case_count++; } prev_impl = the_impl; @@ -3126,8 +3126,8 @@ namespace Duality { axioms_added = true; } else { - //#define KILL_ON_BAD_INTERPOLANT -#ifdef KILL_ON_BAD_INTERPOLANT + //#define KILL_ON_BAD_INTERPOLANT +#ifdef KILL_ON_BAD_INTERPOLANT std::cout << "bad in InterpolateByCase -- core:\n"; #if 0 std::vector assumps; @@ -3137,7 +3137,7 @@ namespace Duality { #endif std::cout << "checking for inconsistency\n"; std::cout << "model:\n"; - is.get_model().show(); + is.get_model().show(); expr impl = is.get_implicant(); std::vector conjuncts; CollectConjuncts(impl,conjuncts,true); @@ -3379,7 +3379,7 @@ namespace Duality { int arity = f.arity(); std::vector domain; for(int i = 0; i < arity; i++) - domain.push_back(f.domain(i)); + domain.push_back(f.domain(i)); return ctx.function(name.c_str(), arity, &domain[0], f.range()); } @@ -3390,7 +3390,7 @@ namespace Duality { int arity = f.arity(); std::vector domain; for(int i = 0; i < arity; i++) - domain.push_back(f.domain(i)); + domain.push_back(f.domain(i)); return ctx.function(name.c_str(), arity, &domain[0], f.range()); } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp old mode 100755 new mode 100644 index 1c441e5d2..4568ea913 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -55,7 +55,7 @@ // #define KEEP_EXPANSIONS // #define USE_CACHING_RPFP // #define PROPAGATE_BEFORE_CHECK -#define NEW_STRATIFIED_INLINING +#define NEW_STRATIFIED_INLINING #define USE_RPFP_CLONE #define USE_NEW_GEN_CANDS @@ -389,7 +389,7 @@ namespace Duality { else InstantiateAllEdges(); } else { -#ifdef NEW_STRATIFIED_INLINING +#ifdef NEW_STRATIFIED_INLINING #else CreateLeaves(); @@ -929,7 +929,7 @@ namespace Duality { int StratifiedLeafCount; -#ifdef NEW_STRATIFIED_INLINING +#ifdef NEW_STRATIFIED_INLINING /** Stratified inlining builds an initial layered unwinding before switching to the LAWI strategy. Currently the number of layers @@ -1135,7 +1135,6 @@ namespace Duality { conj = rpfp->conjoin(conjs); } } - Node *CreateUnderapproxNode(Node *node){ // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); @@ -1872,7 +1871,7 @@ namespace Duality { underapproximations as upper bounds. This mode is used to complete the partial derivation constructed in underapprox mode. - */ + */ bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = 0){ underapprox = _underapprox; @@ -2066,7 +2065,7 @@ namespace Duality { if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ unused_set.resize(orig_unused); if(to - from == 1){ -#if 1 +#if 1 std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; #endif choices.insert(used_set[from]); @@ -2250,14 +2249,14 @@ namespace Duality { throw "stacks out of sync!"; reporter->Depth(stack.size()); - // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop + // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop check_result foo = Check(); res = foo == unsat ? l_false : l_true; if (res == l_false) { if (stack.empty()) // should never happen return false; - + { std::vector &expansions = stack.back().expansions; int update_count = 0; @@ -2384,7 +2383,7 @@ namespace Duality { tree->Pop(1); node_order.clear(); while(stack.size() > 1){ - tree->Pop(1); + tree->Pop(1); std::vector &expansions = stack.back().expansions; for(unsigned i = 0; i < expansions.size(); i++) node_order.push_back(expansions[i]); @@ -2420,12 +2419,12 @@ namespace Duality { void PopLevel(){ std::vector &expansions = stack.back().expansions; - tree->Pop(1); + tree->Pop(1); hash_set leaves_to_remove; for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; - // if(node != top) - // tree->ConstrainParent(node->Incoming[0],node); + // if(node != top) + //tree->ConstrainParent(node->Incoming[0],node); std::vector &cs = node->Outgoing->Children; for(unsigned i = 0; i < cs.size(); i++){ leaves_to_remove.insert(cs[i]); @@ -2441,7 +2440,7 @@ namespace Duality { } stack.pop_back(); } - + bool NodeTooComplicated(Node *node){ int ops = tree->CountOperators(node->Annotation.Formula); if(ops > 10) return true; @@ -2670,7 +2669,7 @@ namespace Duality { } } } -#endif +#endif } @@ -2732,7 +2731,7 @@ namespace Duality { return false; if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) return covering->number < covered->number || parent->underapprox_map[covering] == covered; -#endif +#endif return covering->number < covered->number; } @@ -2778,7 +2777,7 @@ namespace Duality { else return false; } -#endif +#endif bool Close(Node *node){ if(covered_by(node)) @@ -2842,7 +2841,7 @@ namespace Duality { #ifdef UNDERAPPROX_NODES // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) // return parent->underapprox_map[covering] == covered; -#endif +#endif if(CoverOrder(covering,covered) && !IsCovered(covering)){ RPFP::Transformer f(covering->Annotation); f.SetEmpty(); @@ -3175,7 +3174,7 @@ namespace Duality { // else // std::cout << "constant not matched\n"; } - + RPFP *old_unwinding = old_solver->unwinding; hash_map > pred_match; @@ -3467,7 +3466,7 @@ namespace Duality { dnode->Bound.Formula = bound_fmla; } db_saved_bounds.push_back(bound_fmla); - // dnode->Annotation.Formula = ctx.make(And,node->Annotation.Formula,ctx.make(Geq,dvar,ctx.int_val(0))); + // dnode->Annotation.Formula = ctx.make(And,node->Annotation.Formula,ctx.make(Geq,dvar,ctx.int_val(0))); } for(unsigned i = 0; i < rpfp->edges.size(); i++){ Edge *edge = rpfp->edges[i]; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 0cb5df056..3b29adc41 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -562,7 +562,14 @@ namespace Duality { opts.set("weak","1"); ::ast *proof = m_solver->get_proof(); - iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); + try { + iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); + } + // If there's an interpolation bug, throw a char * + // exception so duality can catch it and restart. + catch (const interpolation_failure &f) { + throw f.msg(); + } std::vector linearized_interpolants(_interpolants.size()); for(unsigned i = 0; i < _interpolants.size(); i++) @@ -727,7 +734,7 @@ namespace Duality { if(!started){ sw.start(); started = true; - } + } return sw.get_current_seconds(); } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h old mode 100755 new mode 100644 index 7583d5bf8..a84d8899c --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -17,8 +17,8 @@ --*/ -#ifndef __DUALITY_WRAPPER_H_ -#define __DUALITY_WRAPPER_H_ +#ifndef DUALITY_WRAPPER_H_ +#define DUALITY_WRAPPER_H_ #include #include @@ -1489,4 +1489,3 @@ namespace std { } #endif - diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index bb4f760f5..57188e5ca 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -292,14 +292,14 @@ bool iz3base::is_sat(const std::vector &q, ast &_proof, std::vector &v void iz3base::find_children(const stl_ext::hash_set &cnsts_set, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &conjuncts, - std::vector &children, - std::vector &pos_map, - bool merge - ){ + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &conjuncts, + std::vector &children, + std::vector &pos_map, + bool merge + ){ std::vector my_children; std::vector my_conjuncts; if(op(tree) == Interp){ // if we've hit an interpolation position... @@ -336,13 +336,13 @@ void iz3base::find_children(const stl_ext::hash_set &cnsts_set, } void iz3base::to_parents_vec_representation(const std::vector &_cnsts, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &theory, - std::vector &pos_map, - bool merge - ){ + const ast &tree, + std::vector &cnsts, + std::vector &parents, + std::vector &theory, + std::vector &pos_map, + bool merge + ){ std::vector my_children; std::vector my_conjuncts; hash_set cnsts_set; diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp index 480e85274..69cfea8b8 100755 --- a/src/interp/iz3checker.cpp +++ b/src/interp/iz3checker.cpp @@ -144,7 +144,7 @@ struct iz3checker : iz3base { hash_set tmemo; for(unsigned j = 0; j < theory.size(); j++) support(theory[j],common,tmemo); // all theory symbols allowed in interps - } + } hash_set memo; support(itp[i],Isup,memo); std::set_difference(Isup.begin(),Isup.end(),common.begin(),common.end(),std::inserter(bad,bad.begin())); @@ -192,35 +192,35 @@ std::vector to_std_vector(const ::vector &v){ bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &cnsts, - const ::vector &parents, - const ptr_vector &interps, - const ptr_vector &theory) + solver *s, + std::ostream &err, + const ptr_vector &cnsts, + const ::vector &parents, + const ptr_vector &interps, + const ptr_vector &theory) { iz3checker chk(_m_manager); return chk.check(s,err,chk.cook(cnsts),to_std_vector(parents),chk.cook(interps),chk.cook(theory)); } bool iz3check(iz3mgr &mgr, - solver *s, - std::ostream &err, - const std::vector &cnsts, - const std::vector &parents, - const std::vector &interps, - const std::vector &theory) + solver *s, + std::ostream &err, + const std::vector &cnsts, + const std::vector &parents, + const std::vector &interps, + const std::vector &theory) { iz3checker chk(mgr); return chk.check(s,err,cnsts,parents,interps,theory); } bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &_cnsts, - ast *tree, - const ptr_vector &interps) + solver *s, + std::ostream &err, + const ptr_vector &_cnsts, + ast *tree, + const ptr_vector &interps) { iz3checker chk(_m_manager); return chk.check(s,err,chk.cook(_cnsts),chk.cook(tree),chk.cook(interps)); diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index caf7e6ddc..9cf03b53d 100644 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -44,6 +44,7 @@ namespace hash_space { template class hash {}; + template <> class hash { public: diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index c9b5e7920..00bc5111a 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -252,7 +252,7 @@ public: // create a secondary prover iz3secondary *sp = iz3foci::create(this,num,parents_vec.empty()?0:&parents_vec[0]); sp_killer.set(sp); // kill this on exit - + #define BINARY_INTERPOLATION #ifndef BINARY_INTERPOLATION // create a translator @@ -271,7 +271,18 @@ public: // translate into an interpolatable proof profiling::timer_start("Proof translation"); - tr->translate(proof,pf); + try { + tr->translate(proof,pf); + } + catch (const char *msg) { + throw interpolation_failure(msg); + } + catch (const iz3translation::unsupported &) { + throw interpolation_error(); + } + catch (const iz3proof::proof_error &) { + throw interpolation_error(); + } profiling::timer_stop("Proof translation"); // translate the proof into interpolants @@ -309,7 +320,18 @@ public: // translate into an interpolatable proof profiling::timer_start("Proof translation"); - tr->translate(proof,pf); + try { + tr->translate(proof,pf); + } + catch (const char *msg) { + throw interpolation_failure(msg); + } + catch (const iz3translation::unsupported &) { + throw interpolation_error(); + } + catch (const iz3proof::proof_error &) { + throw interpolation_error(); + } profiling::timer_stop("Proof translation"); // translate the proof into interpolants @@ -420,12 +442,12 @@ public: void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options) + ast *proof, + const ptr_vector &cnsts, + const ::vector &parents, + ptr_vector &interps, + const ptr_vector &theory, + interpolation_options_struct * options) { iz3interp itp(_m_manager); if(options) @@ -448,12 +470,12 @@ void iz3interpolate(ast_manager &_m_manager, } void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ::vector > &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options) + ast *proof, + const ::vector > &cnsts, + const ::vector &parents, + ptr_vector &interps, + const ptr_vector &theory, + interpolation_options_struct * options) { iz3interp itp(_m_manager); if(options) @@ -477,11 +499,11 @@ void iz3interpolate(ast_manager &_m_manager, } void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - ast *tree, - ptr_vector &interps, - interpolation_options_struct * options) + ast *proof, + const ptr_vector &cnsts, + ast *tree, + ptr_vector &interps, + interpolation_options_struct * options) { iz3interp itp(_m_manager); if(options) @@ -506,12 +528,12 @@ void iz3interpolate(ast_manager &_m_manager, } lbool iz3interpolate(ast_manager &_m_manager, - solver &s, - ast *tree, - ptr_vector &cnsts, - ptr_vector &interps, - model_ref &m, - interpolation_options_struct * options) + solver &s, + ast *tree, + ptr_vector &cnsts, + ptr_vector &interps, + model_ref &m, + interpolation_options_struct * options) { iz3interp itp(_m_manager); if(options) diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h index c2668eb56..368c2c25e 100644 --- a/src/interp/iz3interp.h +++ b/src/interp/iz3interp.h @@ -43,6 +43,27 @@ struct iz3_bad_tree { struct iz3_incompleteness { }; +// This is thrown if there is some bug in the +// interpolation procedure +class interpolation_failure : public default_exception { + public: + interpolation_failure(const char *msg) + : default_exception(msg) + { + } +}; + +// This is thrown if we cannot derive an interpolant from a proof +// because it contains unsupported theories or if the proof contains +// errors +class interpolation_error : public default_exception { + public: + interpolation_error() + : default_exception("theory not supported by interpolation or bad proof" ) + { + } +}; + typedef interpolation_options_struct *interpolation_options; /* Compute an interpolant from a proof. This version uses the parents vector diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 5a7f71987..2f7fe1be2 100755 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -556,6 +556,20 @@ void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ extract_lcd(rats); } +void iz3mgr::get_broken_gcd_test_coeffs(const ast &proof, std::vector& rats){ + symb s = sym(proof); + int numps = s->get_num_parameters(); + rats.resize(numps-2); + for(int i = 2; i < numps; i++){ + rational r; + bool ok = s->get_parameter(i).is_rational(r); + if(!ok) + throw "Bad Farkas coefficient"; + rats[i-2] = r; + } + extract_lcd(rats); +} + void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& coeffs){ std::vector rats; get_assign_bounds_coeffs(proof,rats); @@ -602,6 +616,30 @@ void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& r extract_lcd(rats); } +void iz3mgr::get_gomory_cut_coeffs(const ast &proof, std::vector& coeffs){ + std::vector rats; + get_gomory_cut_coeffs(proof,rats); + coeffs.resize(rats.size()); + for(unsigned i = 0; i < rats.size(); i++){ + coeffs[i] = make_int(rats[i]); + } +} + +void iz3mgr::get_gomory_cut_coeffs(const ast &proof, std::vector& rats){ + symb s = sym(proof); + int numps = s->get_num_parameters(); + rats.resize(numps-2); + for(int i = 2; i < numps; i++){ + rational r; + bool ok = s->get_parameter(i).is_rational(r); + if(!ok) + throw "Bad Farkas coefficient"; + rats[i-2] = r; + } + abs_rat(rats); + extract_lcd(rats); +} + void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& coeffs){ std::vector rats; get_assign_bounds_rule_coeffs(proof,rats); @@ -695,6 +733,8 @@ void iz3mgr::linear_comb(ast &P, const ast &c, const ast &Q, bool round_off){ } } bool pstrict = op(P) == Lt; + if (round_off && get_type(Qrhs) != int_type()) + round_off = false; if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ Qrhs = make(Sub,Qrhs,make_int(rational(1))); qstrict = false; diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 3f4138354..dcbe08817 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -279,6 +279,12 @@ class iz3mgr { res[i] = arg(t,i); } + std::vector args(const ast &t){ + std::vector res; + get_args(t,res); + return res; + } + symb sym(ast t){ raw_ast *_ast = t.raw(); return is_app(_ast) ? to_app(_ast)->get_decl() : 0; @@ -390,7 +396,7 @@ class iz3mgr { return UnknownTheory; } - enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,ArithMysteryKind,UnknownKind}; + enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,GomoryCutKind,ArithMysteryKind,UnknownKind}; lemma_kind get_theory_lemma_kind(const ast &proof){ symb s = sym(proof); @@ -411,6 +417,8 @@ class iz3mgr { return AssignBoundsKind; if(foo == "eq-propagate") return EqPropagateKind; + if(foo == "gomory-cut") + return GomoryCutKind; return UnknownKind; } @@ -418,6 +426,8 @@ class iz3mgr { void get_farkas_coeffs(const ast &proof, std::vector& rats); + void get_broken_gcd_test_coeffs(const ast &proof, std::vector& rats); + void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); @@ -426,6 +436,10 @@ class iz3mgr { void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); + void get_gomory_cut_coeffs(const ast &proof, std::vector& rats); + + void get_gomory_cut_coeffs(const ast &proof, std::vector& rats); + bool is_farkas_coefficient_negative(const ast &proof, int n); bool is_true(ast t){ diff --git a/src/interp/iz3pp.cpp b/src/interp/iz3pp.cpp index 26cefedb3..066e04e83 100644 --- a/src/interp/iz3pp.cpp +++ b/src/interp/iz3pp.cpp @@ -107,9 +107,9 @@ public: }; void iz3pp(ast_manager &m, - const ptr_vector &cnsts_vec, - expr *tree, - std::ostream& out) { + const ptr_vector &cnsts_vec, + expr *tree, + std::ostream& out) { unsigned sz = cnsts_vec.size(); expr* const* cnsts = &cnsts_vec[0]; diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 0bdd51ed8..046b0b7f1 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -778,7 +778,7 @@ class iz3proof_itp_impl : public iz3proof_itp { sum_cond_ineq(ineq2,make_int("1"),yleqx,Aproves2,Bproves2); ast Bcond = my_implies(Bproves1,my_and(Aproves1,z3_simplify(ineq2))); // if(!is_true(Aproves1) || !is_true(Bproves1)) - // std::cout << "foo!\n";; + // std::cout << "foo!\n";; if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ if(get_term_type(arg(x,0)) == LitA){ ast iter = z3_simplify(make(Plus,arg(x,0),get_ineq_rhs(xleqy))); @@ -915,7 +915,7 @@ class iz3proof_itp_impl : public iz3proof_itp { int ipos = pos.get_unsigned(); ast cond = mk_true(); ast equa = sep_cond(arg(pf,0),cond); -#if 0 +#if 0 if(op(equa) == Equal){ ast pe = mk_not(neg_equality); ast lhs = subst_in_arg_pos(ipos,arg(equa,0),arg(pe,0)); @@ -2215,8 +2215,12 @@ class iz3proof_itp_impl : public iz3proof_itp { } else { if(get_term_type(p) == LitA){ - if(get_term_type(q) == LitA) - itp = mk_false(); + if(get_term_type(q) == LitA){ + if(op(q) == Or) + itp = make_assumption(rng.hi,args(q)); + else + itp = mk_false(); + } else { if(get_term_type(p_eq_q) == LitA) itp = q; @@ -2580,6 +2584,8 @@ class iz3proof_itp_impl : public iz3proof_itp { if(pstrict && qstrict && round_off) Qrhs = make(Sub,Qrhs,make_int(rational(1))); #else + if (round_off && get_type(Qrhs) != int_type()) + round_off = false; bool pstrict = op(P) == Lt; if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ Qrhs = make(Sub,Qrhs,make_int(rational(1))); @@ -2784,7 +2790,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } else if(o == Plus || o == Times){ // don't want bound variables inside arith ops // std::cout << "WARNING: non-local arithmetic\n"; - // frng = erng; // this term will be localized + // frng = erng; // this term will be localized } else if(o == Select){ // treat the array term like a function symbol prover::range srng = pv->ast_scope(arg(e,0)); @@ -2805,7 +2811,7 @@ class iz3proof_itp_impl : public iz3proof_itp { pfs.push_back(argpf); } } - + e = clone(e,largs); if(pfs.size()) pf = make_congruence(eqs,make_equiv(e,orig_e),pfs); diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp index c365619b2..b70f37a06 100755 --- a/src/interp/iz3scopes.cpp +++ b/src/interp/iz3scopes.cpp @@ -97,7 +97,7 @@ namespace std { template <> inline size_t stdext::hash_value(const scopes::range_lo& p) -{ +{ std::hash h; return h(p); } @@ -133,7 +133,7 @@ namespace std { template <> inline size_t stdext::hash_value(const range_op& p) -{ +{ std::hash h; return h(p); } diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 5b420673c..51084f809 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -274,7 +274,7 @@ public: ast neglit = mk_not(arg(con,i)); res.erase(neglit); } - } + } } } #if 0 @@ -612,7 +612,7 @@ public: rng = range_glb(rng,ast_scope(lit)); } if(range_is_empty(rng)) return -1; - int hi = range_max(rng); + int hi = range_max(rng); if(hi >= frames) return frames - 1; return hi; } @@ -966,7 +966,7 @@ public: get_linear_coefficients(t,coeffs); if(coeffs.size() == 0) return make_int("1"); // arbitrary - rational d = coeffs[0]; + rational d = abs(coeffs[0]); for(unsigned i = 1; i < coeffs.size(); i++){ d = gcd(d,coeffs[i]); } @@ -1021,6 +1021,12 @@ public: my_coeffs.push_back(make_int(c)); my_prem_cons.push_back(conc(prem(proof,i))); } + else if(c.is_neg()){ + int j = (i % 2 == 0) ? i + 1 : i - 1; + my_prems.push_back(prems[j]); + my_coeffs.push_back(make_int(-coeffs[j])); + my_prem_cons.push_back(conc(prem(proof,j))); + } } ast my_con = sum_inequalities(my_coeffs,my_prem_cons); @@ -1176,6 +1182,31 @@ public: return res; } + ast GomoryCutRule2Farkas(const ast &proof, const ast &con, std::vector prems){ + std::vector my_prems = prems; + std::vector my_coeffs; + std::vector my_prem_cons; + get_gomory_cut_coeffs(proof,my_coeffs); + int nargs = num_prems(proof); + if(nargs != (int)(my_coeffs.size())) + throw "bad gomory-cut theory lemma"; + for(int i = 0; i < nargs; i++) + my_prem_cons.push_back(conc(prem(proof,i))); + ast my_con = normalize_inequality(sum_inequalities(my_coeffs,my_prem_cons)); + Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); + my_prems.push_back(hyp); + my_coeffs.push_back(make_int("1")); + my_prem_cons.push_back(mk_not(my_con)); + Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_prem_cons,my_coeffs); + ast t = arg(my_con,0); + ast c = arg(my_con,1); + ast d = gcd_of_coefficients(t); + t = z3_simplify(mk_idiv(t,d)); + c = z3_simplify(mk_idiv(c,d)); + ast cut_con = make(op(my_con),t,c); + return iproof->make_cut_rule(my_con,d,cut_con,res); + } + Iproof::node RewriteClause(Iproof::node clause, const ast &rew){ if(pr(rew) == PR_MONOTONICITY){ int nequivs = num_prems(rew); @@ -1376,6 +1407,7 @@ public: hash_map dual_map; std::vector cvec, vars_seen; + m().enable_int_real_coercions(true); ast rhs = make_real(rational(0)); for(unsigned i = 0; i < npcons.size(); i++){ ast c= mk_fresh_constant("@c",real_type()); @@ -1702,14 +1734,16 @@ public: return res; } } - if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or){ + if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or && op(conc(prem(proof,0))) == Or){ Iproof::node clause = translate_main(prem(proof,0),true); res = RewriteClause(clause,prem(proof,1)); return res; } +#if 0 if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) std::cout << "foo!\n"; +#endif // no idea why this shows up if(dk == PR_MODUS_PONENS_OEQ){ @@ -1882,7 +1916,7 @@ public: } case GCDTestKind: { std::vector farkas_coeffs; - get_farkas_coeffs(proof,farkas_coeffs); + get_broken_gcd_test_coeffs(proof,farkas_coeffs); if(farkas_coeffs.size() != nprems){ pfgoto(proof); throw unsupported(); @@ -1904,6 +1938,13 @@ public: res = AssignBounds2Farkas(proof,conc(proof)); break; } + case GomoryCutKind: { + if(args.size() > 0) + res = GomoryCutRule2Farkas(proof, conc(proof), args); + else + throw unsupported(); + break; + } case EqPropagateKind: { std::vector prems(nprems); for(unsigned i = 0; i < nprems; i++) @@ -1965,6 +2006,17 @@ public: res = make(commute,pf,comm_equiv); break; } + case PR_NOT_OR_ELIM: + case PR_AND_ELIM: { + std::vector rule_ax, res_conc; + ast piv = conc(prem(proof,0)); + rule_ax.push_back(make(Not,piv)); + rule_ax.push_back(con); + ast pf = iproof->make_axiom(rule_ax); + res_conc.push_back(con); + res = iproof->make_resolution(piv,res_conc,pf,args[0]); + break; + } default: pfgoto(proof); assert(0 && "translate_main: unsupported proof rule"); @@ -1991,11 +2043,23 @@ public: locality.clear(); #endif iproof = iz3proof_itp::create(this,range_downward(i),weak_mode()); - Iproof::node ipf = translate_main(proof); - ast itp = iproof->interpolate(ipf); - itps.push_back(itp); - delete iproof; - clear_translation(); + try { + Iproof::node ipf = translate_main(proof); + ast itp = iproof->interpolate(ipf); + itps.push_back(itp); + delete iproof; + clear_translation(); + } + catch (const iz3proof_itp::proof_error &) { + delete iproof; + clear_translation(); + throw iz3proof::proof_error(); + } + catch (const unsupported &exc) { + delete iproof; + clear_translation(); + throw exc; + } } // Very simple proof -- lemma of the empty clause with computed interpolation iz3proof::node Ipf = dst.make_lemma(std::vector(),itps); // builds result in dst @@ -2003,10 +2067,10 @@ public: } iz3translation_full(iz3mgr &mgr, - iz3secondary *_secondary, + iz3secondary *_secondary, const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory) + const std::vector &parents, + const std::vector &theory) : iz3translation(mgr, cnsts, parents, theory) { frames = cnsts.size(); @@ -2027,10 +2091,10 @@ public: #ifdef IZ3_TRANSLATE_FULL iz3translation *iz3translation::create(iz3mgr &mgr, - iz3secondary *secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory){ + iz3secondary *secondary, + const std::vector > &cnsts, + const std::vector &parents, + const std::vector &theory){ return new iz3translation_full(mgr,secondary,cnsts,parents,theory); } diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index c1b751c6f..ec3af1ca3 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -293,7 +293,7 @@ public: ast neglit = mk_not(arg(con,i)); res.erase(neglit); } - } + } } } #if 0 @@ -589,7 +589,7 @@ public: rng = range_glb(rng,ast_scope(lit)); } if(range_is_empty(rng)) return -1; - int hi = range_max(rng); + int hi = range_max(rng); if(hi >= frames) return frames - 1; return hi; } @@ -881,7 +881,7 @@ public: || dk == PR_QUANT_INST //|| dk == PR_UNIT_RESOLUTION //|| dk == PR_LEMMA - ) + ) return false; if(dk == PR_HYPOTHESIS && hyps.find(con) != hyps.end()) ; //std::cout << "blif!\n"; @@ -1481,7 +1481,7 @@ public: AstSet dummy; collect_resolvent_lits(nll->proofs[i],dummy,reslits); litset.insert(reslits.begin(),reslits.end()); - } + } } if(to_keep.size() == nll->proofs.size()) return nll; ResolventAppSet new_proofs; diff --git a/src/math/euclid/euclidean_solver.cpp b/src/math/euclid/euclidean_solver.cpp index 718dbb052..9225a87ee 100644 --- a/src/math/euclid/euclidean_solver.cpp +++ b/src/math/euclid/euclidean_solver.cpp @@ -609,6 +609,7 @@ struct euclidean_solver::imp { // neg coeffs... to make sure that m_next_x is -1 neg_coeffs(eq.m_as); neg_coeffs(eq.m_bs); + m().neg(eq.m_c); } unsigned sz = eq.size(); for (unsigned i = 0; i < sz; i++) { @@ -717,7 +718,7 @@ struct euclidean_solver::imp { elim_unit(); else decompose_and_elim(); - TRACE("euclidean_solver_step", display(tout);); + TRACE("euclidean_solver", display(tout);); if (inconsistent()) return false; } return true; diff --git a/src/math/euclid/euclidean_solver.h b/src/math/euclid/euclidean_solver.h index f2345b9ed..839abc9f7 100644 --- a/src/math/euclid/euclidean_solver.h +++ b/src/math/euclid/euclidean_solver.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _EUCLIDEAN_SOLVER_H_ -#define _EUCLIDEAN_SOLVER_H_ +#ifndef EUCLIDEAN_SOLVER_H_ +#define EUCLIDEAN_SOLVER_H_ #include"mpq.h" #include"vector.h" diff --git a/src/math/grobner/grobner.cpp b/src/math/grobner/grobner.cpp index 11bebf6c5..3bca8c3e7 100644 --- a/src/math/grobner/grobner.cpp +++ b/src/math/grobner/grobner.cpp @@ -59,15 +59,19 @@ void grobner::del_equation(equation * eq) { m_to_process.erase(eq); SASSERT(m_equations_to_delete[eq->m_bidx] == eq); m_equations_to_delete[eq->m_bidx] = 0; - ptr_vector::iterator it1 = eq->m_monomials.begin(); - ptr_vector::iterator end1 = eq->m_monomials.end(); - for (; it1 != end1; ++it1) { - monomial * m = *it1; - del_monomial(m); - } + del_monomials(eq->m_monomials); dealloc(eq); } +void grobner::del_monomials(ptr_vector& ms) { + ptr_vector::iterator it = ms.begin(); + ptr_vector::iterator end = ms.end(); + for (; it != end; ++it) { + del_monomial(*it); + } + ms.reset(); +} + void grobner::del_monomial(monomial * m) { ptr_vector::iterator it2 = m->m_vars.begin(); ptr_vector::iterator end2 = m->m_vars.end(); @@ -436,16 +440,19 @@ void grobner::merge_monomials(ptr_vector & monomials) { unsigned sz = monomials.size(); if (sz == 0) return; + SASSERT(&m_del_monomials != &monomials); + ptr_vector& to_delete = m_del_monomials; + to_delete.reset(); for (unsigned i = 1; i < sz; ++i) { monomial * m1 = monomials[j]; monomial * m2 = monomials[i]; if (is_eq_monomial_body(m1, m2)) { m1->m_coeff += m2->m_coeff; - del_monomial(m2); + to_delete.push_back(m2); } else { if (m1->m_coeff.is_zero()) - del_monomial(m1); // cancelled + to_delete.push_back(m1); else j++; monomials[j] = m2; @@ -454,10 +461,11 @@ void grobner::merge_monomials(ptr_vector & monomials) { SASSERT(j < sz); monomial * m1 = monomials[j]; if (m1->m_coeff.is_zero()) - del_monomial(m1); // cancelled + to_delete.push_back(m1); else j++; monomials.shrink(j); + del_monomials(to_delete); TRACE("grobner", tout << "after merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";); } @@ -649,6 +657,7 @@ grobner::equation * grobner::simplify(equation const * source, equation * target target->m_lc = false; mul_append(1, source, coeff, rest, new_monomials); del_monomial(curr); + target->m_monomials[i] = 0; } else { target->m_monomials[j] = curr; diff --git a/src/math/grobner/grobner.h b/src/math/grobner/grobner.h index d69a3ac0a..fa200f70d 100644 --- a/src/math/grobner/grobner.h +++ b/src/math/grobner/grobner.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _GROBNER_H_ -#define _GROBNER_H_ +#ifndef GROBNER_H_ +#define GROBNER_H_ #include"ast.h" #include"arith_decl_plugin.h" @@ -110,6 +110,7 @@ protected: }; svector m_scopes; ptr_vector m_tmp_monomials; + ptr_vector m_del_monomials; ptr_vector m_tmp_vars1; ptr_vector m_tmp_vars2; unsigned m_num_new_equations; // temporary variable @@ -126,6 +127,8 @@ protected: void del_equations(unsigned old_size); + void del_monomials(ptr_vector& monomials); + void unfreeze_equations(unsigned old_size); void del_equation(equation * eq); @@ -277,5 +280,5 @@ public: void display(std::ostream & out) const; }; -#endif /* _GROBNER_H_ */ +#endif /* GROBNER_H_ */ diff --git a/src/math/hilbert/heap_trie.h b/src/math/hilbert/heap_trie.h index c2a1c52d1..ab55a44c3 100644 --- a/src/math/hilbert/heap_trie.h +++ b/src/math/hilbert/heap_trie.h @@ -34,8 +34,8 @@ Notes: --*/ -#ifndef _HEAP_TRIE_H_ -#define _HEAP_TRIE_H_ +#ifndef HEAP_TRIE_H_ +#define HEAP_TRIE_H_ #include "map.h" #include "vector.h" diff --git a/src/math/hilbert/hilbert_basis.h b/src/math/hilbert/hilbert_basis.h index 733b3a2f0..4969b2c61 100644 --- a/src/math/hilbert/hilbert_basis.h +++ b/src/math/hilbert/hilbert_basis.h @@ -25,8 +25,8 @@ Revision History: --*/ -#ifndef _HILBERT_BASIS_H_ -#define _HILBERT_BASIS_H_ +#ifndef HILBERT_BASIS_H_ +#define HILBERT_BASIS_H_ #include "rational.h" #include "lbool.h" diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index ed7654f01..805cb3fda 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _INTERVAL_H_ -#define _INTERVAL_H_ +#ifndef INTERVAL_H_ +#define INTERVAL_H_ #include"mpq.h" #include"ext_numeral.h" diff --git a/src/math/interval/interval_def.h b/src/math/interval/interval_def.h index 89d699f1f..e529ceceb 100644 --- a/src/math/interval/interval_def.h +++ b/src/math/interval/interval_def.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _INTERVAL_DEF_H_ -#define _INTERVAL_DEF_H_ +#ifndef INTERVAL_DEF_H_ +#define INTERVAL_DEF_H_ #include"interval.h" #include"debug.h" diff --git a/src/math/polynomial/algebraic_numbers.h b/src/math/polynomial/algebraic_numbers.h index d11237b86..2c63c9718 100644 --- a/src/math/polynomial/algebraic_numbers.h +++ b/src/math/polynomial/algebraic_numbers.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _ALGEBRAIC_NUMBERS_H_ -#define _ALGEBRAIC_NUMBERS_H_ +#ifndef ALGEBRAIC_NUMBERS_H_ +#define ALGEBRAIC_NUMBERS_H_ #include"rational.h" #include"mpq.h" diff --git a/src/math/polynomial/linear_eq_solver.h b/src/math/polynomial/linear_eq_solver.h index e7dd6700b..99f418fce 100644 --- a/src/math/polynomial/linear_eq_solver.h +++ b/src/math/polynomial/linear_eq_solver.h @@ -19,8 +19,8 @@ Author: Notes: --*/ -#ifndef _LINEAR_EQ_SOLVER_H_ -#define _LINEAR_EQ_SOLVER_H_ +#ifndef LINEAR_EQ_SOLVER_H_ +#define LINEAR_EQ_SOLVER_H_ template class linear_eq_solver { diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 8f4c55392..5599156d5 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -99,8 +99,17 @@ namespace polynomial { void set_var(var x) { first = x; } struct lt_var { - bool operator()(power const & p1, power const & p2) { - SASSERT(p1.get_var() != p2.get_var()); + bool operator()(power const & p1, power const & p2) { + // CMW: The assertion below does not hold on OSX, because + // their implementation of std::sort will try to compare + // two items at the same index instead of comparing + // the indices directly. I suspect that the purpose of + // this assertion was to make sure that there are + // no duplicates, so I replaced it with a new assertion at + // the end of var_degrees(...). + + // SASSERT(p1.get_var() != p2.get_var()); + return p1.get_var() < p2.get_var(); } }; @@ -542,6 +551,7 @@ namespace polynomial { increase_capacity(sz * 2); SASSERT(sz < m_capacity); m_ptr->m_size = sz; + if (sz == 0) return; memcpy(m_ptr->m_powers, pws, sizeof(power) * sz); } @@ -1652,12 +1662,12 @@ namespace polynomial { } void manager::factors::display(std::ostream & out) const { - out << m().m().to_string(get_constant()); - for (unsigned i = 0; i < m_factors.size(); ++ i) { - out << " * ("; - m_manager.display(out, m_factors[i]); - out << ")^" << m_degrees[i]; - } + out << m().m().to_string(get_constant()); + for (unsigned i = 0; i < m_factors.size(); ++ i) { + out << " * ("; + m_manager.display(out, m_factors[i]); + out << ")^" << m_degrees[i]; + } } void manager::factors::set_constant(numeral const & constant) { @@ -3241,6 +3251,13 @@ namespace polynomial { SASSERT(var2pos[pws[i].get_var()] != UINT_MAX); var2pos[pws[i].get_var()] = UINT_MAX; } + + DEBUG_CODE({ + for (unsigned i = 0; i < pws.size(); i++) { + for (unsigned j = i + 1; j < pws.size(); j++) + SASSERT(pws[i].first != pws[j].first); + } + }); } void var_max_degrees(polynomial const * p, power_buffer & pws) { diff --git a/src/math/polynomial/polynomial.h b/src/math/polynomial/polynomial.h index e52dd4fee..3a4442e63 100644 --- a/src/math/polynomial/polynomial.h +++ b/src/math/polynomial/polynomial.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _POLYNOMIAL_H_ -#define _POLYNOMIAL_H_ +#ifndef POLYNOMIAL_H_ +#define POLYNOMIAL_H_ #include"mpz.h" #include"rational.h" diff --git a/src/math/polynomial/polynomial_cache.h b/src/math/polynomial/polynomial_cache.h index d3b695f5a..538a08b5c 100644 --- a/src/math/polynomial/polynomial_cache.h +++ b/src/math/polynomial/polynomial_cache.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _POLYNOMIAL_CACHE_H_ -#define _POLYNOMIAL_CACHE_H_ +#ifndef POLYNOMIAL_CACHE_H_ +#define POLYNOMIAL_CACHE_H_ #include"polynomial.h" diff --git a/src/math/polynomial/polynomial_factorization.cpp b/src/math/polynomial/polynomial_factorization.cpp index 220048a9a..4bf227d44 100644 --- a/src/math/polynomial/polynomial_factorization.cpp +++ b/src/math/polynomial/polynomial_factorization.cpp @@ -172,8 +172,8 @@ void multivariate_factor_coefficient_bound(polynomial_ref const & f, var x, nume // check that A*S+B*T=C in zp mod ideal bool check_solve(zp_manager & zp_pm, var2degree const & ideal, - zp_polynomial_ref const & A, zp_polynomial_ref const & B, zp_polynomial_ref const & C, - zp_polynomial_ref const & S, zp_polynomial_ref const & T) { + zp_polynomial_ref const & A, zp_polynomial_ref const & B, zp_polynomial_ref const & C, + zp_polynomial_ref const & S, zp_polynomial_ref const & T) { zp_polynomial_ref AS(zp_pm), BT(zp_pm), sum(zp_pm); AS = zp_pm.mul(A, S); AS = zp_pm.mod_d(AS, ideal); BT = zp_pm.mul(B, T); BT = zp_pm.mod_d(BT, ideal); @@ -266,7 +266,7 @@ void solve(zp_manager & zp_pm, var x, var2degree const & ideal, * all polynomials in Z_p[x, ..., y] mod (..., y^d) */ void multivariate_hansel_lift_ideal( - zp_manager & zp_pm, var x, + zp_manager & zp_pm, var x, zp_polynomial_ref const & C, zp_polynomial_ref & A, zp_polynomial_ref & U, zp_polynomial_ref & B, zp_polynomial_ref & V, var2degree & ideal, var y, unsigned d) { @@ -333,7 +333,7 @@ void multivariate_hansel_lift_ideal( tout << "A = " << A << endl; tout << "B = " << B << endl; ); - + // get the S, T, solution to A*S+B*T = f, with d(T, x) < d(A, x) // but we know that S = U*f + Bt, T = V*f - At, so we do division zp_polynomial_ref S(zp_pm), T(zp_pm); @@ -415,9 +415,9 @@ void multivariate_hansel_lift_ideal( template bool are_equal_in( - manager_to_check pm, - typename manager_1::polynomial_ref const & A, - typename manager_2::polynomial_ref const & B) { + manager_to_check pm, + typename manager_1::polynomial_ref const & A, + typename manager_2::polynomial_ref const & B) { typename manager_to_check::polynomial_ref A_pm(pm), B_pm(pm); A_pm = convert(A.m(), A, pm); @@ -436,16 +436,16 @@ bool are_equal_in( A_lifted*B_lifted = f mod x_i^d_i, p^e */ void multivariate_hansel_lift_zp( - manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, - zp_polynomial_ref const & C_pe, var x, unsigned e, + manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, + zp_polynomial_ref const & C_pe, var x, unsigned e, zp_polynomial_ref const & A_p, zp_polynomial_ref const & U_p, zp_polynomial_ref const & B_p, zp_polynomial_ref const & V_p, - var2degree const & ideal, - zp_polynomial_ref & A_lifted, zp_polynomial_ref & B_lifted) { + var2degree const & ideal, + zp_polynomial_ref & A_lifted, zp_polynomial_ref & B_lifted) { TRACE("polynomial::factorization::multivariate", tout << "polynomial::multiratiate_hensel_lift_zp:" << endl; tout << "zp_pm = Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; - tout << "zpe_pm = Z_" << zpe_pm.m().m().to_string(zpe_pm.m().p()) << endl; + tout << "zpe_pm = Z_" << zpe_pm.m().m().to_string(zpe_pm.m().p()) << endl; tout << "x = x" << x << endl; tout << "ideal = " << ideal << endl; tout << "C_pe = " << C_pe << "," << endl; @@ -483,8 +483,8 @@ void multivariate_hansel_lift_zp( U_pk = convert(zp_pm, U_p, zpk_pm); V_pk = convert(zp_pm, V_p, zpk_pm); - TRACE("polynomial::factorization::multivariate", - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; + TRACE("polynomial::factorization::multivariate", + tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "A_pk = " << A_pk << endl; tout << "B_pk = " << B_pk << endl; tout << "U_pk = " << U_pk << endl; @@ -509,8 +509,8 @@ void multivariate_hansel_lift_zp( f_in_Z = pm.mod_d(f_in_Z, ideal); f_pk = convert(pm, f_in_Z, zpk_pm); - TRACE("polynomial::factorization::multivariate", - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; + TRACE("polynomial::factorization::multivariate", + tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "f_pk = " << f_pk << endl; ); @@ -523,8 +523,8 @@ void multivariate_hansel_lift_zp( polynomial_ref S_in_Z(pm), T_in_Z(pm); solve(zpk_pm, x, ideal, A_pk, U_pk, B_pk, V_pk, f_pk, pm, T_in_Z, S_in_Z); - TRACE("polynomial::factorization::multivariate", - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; + TRACE("polynomial::factorization::multivariate", + tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "S_in_Z = " << S_in_Z << endl; tout << "T_in_Z = " << T_in_Z << endl; ); @@ -540,15 +540,15 @@ void multivariate_hansel_lift_zp( B_next_in_Z = convert(zpk_pm, B_pk, pm); B_next_in_Z = pm.add(B_next_in_Z, T_in_Z); - TRACE("polynomial::factorization::multivariate", - tout << "pk = " << nm.to_string(pk) << endl; - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - tout << "S_in_Z = " << S_in_Z << endl; - tout << "T_in_Z = " << T_in_Z << endl; - tout << "A_pk = " << A_pk << endl; - tout << "B_pk = " << B_pk << endl; - tout << "A_next_in_Z = " << A_next_in_Z << endl; - tout << "B_next_in_Z = " << B_next_in_Z << endl; + TRACE("polynomial::factorization::multivariate", + tout << "pk = " << nm.to_string(pk) << endl; + tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; + tout << "S_in_Z = " << S_in_Z << endl; + tout << "T_in_Z = " << T_in_Z << endl; + tout << "A_pk = " << A_pk << endl; + tout << "B_pk = " << B_pk << endl; + tout << "A_next_in_Z = " << A_next_in_Z << endl; + tout << "B_next_in_Z = " << B_next_in_Z << endl; ); bool eq1 = are_equal_in(zpk_pm, A_next_in_Z, A_pk); @@ -574,9 +574,9 @@ void multivariate_hansel_lift_zp( f_in_Z = pm.sub(f_in_Z, UA_in_Z); f_in_Z = pm.sub(f_in_Z, BV_in_Z); - TRACE("polynomial::factorization::multivariate", - tout << "pk = " << nm.to_string(pk) << endl; - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; + TRACE("polynomial::factorization::multivariate", + tout << "pk = " << nm.to_string(pk) << endl; + tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "U_pk_in_Z = " << U_pk_in_Z << endl; tout << "V_pk_in_Z = " << V_pk_in_Z << endl; tout << "UA_in_Z = " << UA_in_Z << endl; @@ -589,10 +589,10 @@ void multivariate_hansel_lift_zp( // get the S, T, solution to A*S+B*T = f, with d(T, x) < d(A, x) solve(zpk_pm, x, ideal, A_pk, U_pk, B_pk, V_pk, f_pk, pm, S_in_Z, T_in_Z); - - TRACE("polynomial::factorization::multivariate", - tout << "pk = " << nm.to_string(pk) << endl; - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; + + TRACE("polynomial::factorization::multivariate", + tout << "pk = " << nm.to_string(pk) << endl; + tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; tout << "S_in_Z = " << S_in_Z << endl; tout << "T_in_Z = " << T_in_Z << endl; ); @@ -601,7 +601,7 @@ void multivariate_hansel_lift_zp( scoped_numeral next_pk(nm); nm.mul(pk, pk, next_pk); zpk_nm.set_p(next_pk); - + TRACE("polynomial::factorization::multivariate", tout << "zp_pk = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; ); @@ -618,7 +618,7 @@ void multivariate_hansel_lift_zp( U_pk = zpk_pm.add(U_pk, S_pk); // lift V zp_polynomial_ref T_pk(zpk_pm); - T_in_Z = pm.mul(pk, T_in_Z); + T_in_Z = pm.mul(pk, T_in_Z); T_pk = convert(pm, T_in_Z, zpk_pm); TRACE("polynomial::factorization::multivariate", @@ -667,7 +667,7 @@ void multivariate_hansel_lift_zp( A_lifted*B_lifted = f mod x_i^d_i, p^e */ void multivariate_hensel_lift( - manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, + manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, zp_polynomial_ref const & f, var x, unsigned e, var_vector const & all_vars, upolynomial::zp_manager & zp_upm, upolynomial::numeral_vector const & U, upolynomial::numeral_vector const & A, @@ -746,7 +746,7 @@ void multivariate_hensel_lift( i.e. such that f congruent to the new factors. output goes to f_zpe factors. */ void multivariate_hensel_lift( - manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, + manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, polynomial_ref const & f, var x, unsigned e, var_vector const & all_vars, upolynomial::manager & upm, upolynomial::numeral_vector const & f_u, upolynomial::zp_factors const & f_u_zp_factors, @@ -777,9 +777,9 @@ void multivariate_hensel_lift( f_target_zpe = convert(pm, f, zpe_pm); f_target_zpe = zpe_pm.mod_d(f_target_zpe, target_ideal); - TRACE("polynomial::factorization::multivariate", - tout << "target_ideal = " << target_ideal << endl; - tout << "f_target_zpe = " << f_target_zpe << endl; + TRACE("polynomial::factorization::multivariate", + tout << "target_ideal = " << target_ideal << endl; + tout << "f_target_zpe = " << f_target_zpe << endl; ); // we do the product by doing individual lifting like in the univarate case @@ -807,7 +807,7 @@ void multivariate_hensel_lift( // and get the U, V, such that A*U+B*V = 1 zp_upm.ext_gcd(A_u, B_u, U, V, tmp_u); - TRACE("polynomial::factorization::multivariate", + TRACE("polynomial::factorization::multivariate", tout << "U = "; upm.display(tout, U); tout << endl; tout << "V = "; upm.display(tout, V); tout << endl; tout << "gcd = "; upm.display(tout, tmp_u); tout << endl; @@ -943,7 +943,7 @@ bool factor_square_free_primitive(polynomial_ref const & f, factors & f_factors) // to start with, maybe this should be part of input var x = pm.max_var(f); // get all the variables - var_vector vars, vars_no_x; + var_vector vars, vars_no_x; pm.vars(f, vars); for(unsigned i = 0; i < vars.size(); ++ i) { if (vars[i] != x) { @@ -969,7 +969,7 @@ bool factor_square_free_primitive(polynomial_ref const & f, factors & f_factors) unsigned size = 1; a.resize(vars_no_x.size()); for (unsigned i = 0; i < a.size(); ++ i) { nm.reset(a[i]); } - generate_substitution_values(f, x, f_lc, vars_no_x, size, a, upm, f_u); + generate_substitution_values(f, x, f_lc, vars_no_x, size, a, upm, f_u); TRACE("polynomial::factorization::multivariate", tout << "f_u = "; upm.display(tout, f_u); tout << endl; @@ -1068,7 +1068,7 @@ bool factor_square_free_primitive(polynomial_ref const & f, factors & f_factors) multivariate_hensel_lift(pm, zp_pm, zpe_pm, f_t, x, e, vars, upm, f_u_pp_zp, factors_u_zp, target_ideal, factors_zpe); TRACE("polynomial::factorization::multivariate", - tout << "factors_zpe = " << factors_zpe << endl; + tout << "factors_zpe = " << factors_zpe << endl; ); // try the factors from the lifted combinations diff --git a/src/math/polynomial/polynomial_primes.h b/src/math/polynomial/polynomial_primes.h index 9b74c879c..c5afcb727 100644 --- a/src/math/polynomial/polynomial_primes.h +++ b/src/math/polynomial/polynomial_primes.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _POLYNOMIAL_PRIMES_H_ -#define _POLYNOMIAL_PRIMES_H_ +#ifndef POLYNOMIAL_PRIMES_H_ +#define POLYNOMIAL_PRIMES_H_ namespace polynomial { #define NUM_SMALL_PRIMES 11 diff --git a/src/math/polynomial/polynomial_var2value.h b/src/math/polynomial/polynomial_var2value.h index 708b03725..7da2e5980 100644 --- a/src/math/polynomial/polynomial_var2value.h +++ b/src/math/polynomial/polynomial_var2value.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _POLYNOMIAL_VAR2VALUE_H_ -#define _POLYNOMIAL_VAR2VALUE_H_ +#ifndef POLYNOMIAL_VAR2VALUE_H_ +#define POLYNOMIAL_VAR2VALUE_H_ #include"polynomial.h" #include"scoped_numeral_vector.h" diff --git a/src/math/polynomial/rpolynomial.h b/src/math/polynomial/rpolynomial.h index 146505887..de2140c5e 100644 --- a/src/math/polynomial/rpolynomial.h +++ b/src/math/polynomial/rpolynomial.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _RPOLYNOMIAL_H_ -#define _RPOLYNOMIAL_H_ +#ifndef RPOLYNOMIAL_H_ +#define RPOLYNOMIAL_H_ #include"mpz.h" #include"rational.h" diff --git a/src/math/polynomial/sexpr2upolynomial.h b/src/math/polynomial/sexpr2upolynomial.h index 133c2dc6c..64488b0f4 100644 --- a/src/math/polynomial/sexpr2upolynomial.h +++ b/src/math/polynomial/sexpr2upolynomial.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _SEXPR2UPOLYNOMIAL_H_ -#define _SEXPR2UPOLYNOMIAL_H_ +#ifndef SEXPR2UPOLYNOMIAL_H_ +#define SEXPR2UPOLYNOMIAL_H_ #include"upolynomial.h" #include"cmd_context_types.h" diff --git a/src/math/polynomial/upolynomial.h b/src/math/polynomial/upolynomial.h index 697385a10..32214c5db 100644 --- a/src/math/polynomial/upolynomial.h +++ b/src/math/polynomial/upolynomial.h @@ -21,8 +21,8 @@ Author: Notes: --*/ -#ifndef _UPOLYNOMIAL_H_ -#define _UPOLYNOMIAL_H_ +#ifndef UPOLYNOMIAL_H_ +#define UPOLYNOMIAL_H_ #include"mpzzp.h" #include"rational.h" diff --git a/src/math/polynomial/upolynomial_factorization.cpp b/src/math/polynomial/upolynomial_factorization.cpp index 7d5a23d99..0b2977a22 100644 --- a/src/math/polynomial/upolynomial_factorization.cpp +++ b/src/math/polynomial/upolynomial_factorization.cpp @@ -719,12 +719,11 @@ void hensel_lift_quadratic(z_manager& upm, numeral_vector const & C, // we create a new Z_p manager, since we'll be changing the input one zp_manager zp_upm(nm); zp_upm.set_zp(zpe_upm.m().p()); - zp_numeral_manager & zp_nm = zp_upm.m(); // get the U, V, such that A*U + B*V = 1 (mod p) scoped_mpz_vector U(nm), V(nm), D(nm); zp_upm.ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D); - SASSERT(D.size() == 1 && zp_nm.is_one(D[0])); + SASSERT(D.size() == 1 && zp_upm.m().is_one(D[0])); // we start lifting from (a = p, b = p, r = p) scoped_mpz_vector A_lifted(nm), B_lifted(nm); diff --git a/src/math/polynomial/upolynomial_factorization.h b/src/math/polynomial/upolynomial_factorization.h index 327fffc9c..ba184d613 100644 --- a/src/math/polynomial/upolynomial_factorization.h +++ b/src/math/polynomial/upolynomial_factorization.h @@ -22,8 +22,8 @@ Notes: [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ -#ifndef _UPOLYNOMIAL_FACTORIZATION_H_ -#define _UPOLYNOMIAL_FACTORIZATION_H_ +#ifndef UPOLYNOMIAL_FACTORIZATION_H_ +#define UPOLYNOMIAL_FACTORIZATION_H_ #include"upolynomial.h" #include"polynomial.h" diff --git a/src/math/polynomial/upolynomial_factorization_int.h b/src/math/polynomial/upolynomial_factorization_int.h index 92d62301d..86fe52b03 100644 --- a/src/math/polynomial/upolynomial_factorization_int.h +++ b/src/math/polynomial/upolynomial_factorization_int.h @@ -23,8 +23,8 @@ Notes: [3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993. --*/ -#ifndef _UPOLYNOMIAL_FACTORIZATION_INT_H_ -#define _UPOLYNOMIAL_FACTORIZATION_INT_H_ +#ifndef UPOLYNOMIAL_FACTORIZATION_INT_H_ +#define UPOLYNOMIAL_FACTORIZATION_INT_H_ #include"upolynomial_factorization.h" diff --git a/src/math/realclosure/mpz_matrix.h b/src/math/realclosure/mpz_matrix.h index b48585c60..bd9c1bd45 100644 --- a/src/math/realclosure/mpz_matrix.h +++ b/src/math/realclosure/mpz_matrix.h @@ -28,8 +28,8 @@ Author: Notes: --*/ -#ifndef _MPZ_MATRIX_H_ -#define _MPZ_MATRIX_H_ +#ifndef MPZ_MATRIX_H_ +#define MPZ_MATRIX_H_ #include"mpz.h" diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 84ba606fd..1ca8823a1 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -4075,7 +4075,7 @@ namespace realclosure { void refine_rational_interval(rational_value * v, unsigned prec) { mpbqi & i = interval(v); - if (!i.lower_is_open() && !i.lower_is_open()) { + if (!i.lower_is_open() && !i.upper_is_open()) { SASSERT(bqm().eq(i.lower(), i.upper())); return; } diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 2a1b0dc20..4fef4dc25 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -19,8 +19,8 @@ Author: Notes: --*/ -#ifndef _REALCLOSURE_H_ -#define _REALCLOSURE_H_ +#ifndef REALCLOSURE_H_ +#define REALCLOSURE_H_ #include"mpq.h" #include"params.h" diff --git a/src/math/simplex/network_flow.h b/src/math/simplex/network_flow.h new file mode 100644 index 000000000..13aff5089 --- /dev/null +++ b/src/math/simplex/network_flow.h @@ -0,0 +1,200 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + network_flow.h + +Abstract: + + Implements Network Simplex algorithm for min cost flow problem + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-24 + +Notes: + + This will be used to solve the dual of min cost flow problem + i.e. optimization of difference constraint. + + We need a function to reduce DL constraints to min cost flow problem + and another function to convert from min cost flow solution to DL solution. + + It remains unclear how to convert DL assignment to a basic feasible solution of Network Simplex. + A naive approach is to run an algorithm on max flow in order to get a spanning tree. + +--*/ +#ifndef NETWORK_FLOW_H_ +#define NETWORK_FLOW_H_ + +#include"inf_rational.h" +#include"diff_logic.h" +#include"spanning_tree.h" + +namespace smt { + + enum min_flow_result { + // Min cost flow problem is infeasible. + // Diff logic optimization could be unbounded or infeasible. + INFEASIBLE, + // Min cost flow and diff logic optimization are both optimal. + OPTIMAL, + // Min cost flow problem is unbounded. + // Diff logic optimization has to be infeasible. + UNBOUNDED, + }; + + enum pivot_rule { + // First eligible edge pivot rule + // Edges are traversed in a wraparound fashion + FIRST_ELIGIBLE, + // Best eligible edge pivot rule + // The best edge is selected in every iteration + BEST_ELIGIBLE, + // Candidate list pivot rule + // Major iterations: candidate list is built from eligible edges (in a wraparound way) + // Minor iterations: the best edge is selected from the list + CANDIDATE_LIST + }; + + // Solve minimum cost flow problem using Network Simplex algorithm + template + class network_flow : private Ext { + private: + enum edge_state { + LOWER = 1, + BASIS = 0, + }; + + typedef dl_var node; + typedef dl_edge edge; + typedef dl_graph graph; + typedef typename Ext::numeral numeral; + typedef typename Ext::fin_numeral fin_numeral; + + class pivot_rule_impl { + protected: + graph & m_graph; + svector & m_states; + vector & m_potentials; + edge_id & m_enter_id; + bool edge_in_tree(edge_id id) const { return m_states[id] == BASIS; } + public: + pivot_rule_impl(graph & g, vector & potentials, + svector & states, edge_id & enter_id) + : m_graph(g), + m_potentials(potentials), + m_states(states), + m_enter_id(enter_id) { + } + virtual ~pivot_rule_impl() {} + virtual bool choose_entering_edge() = 0; + virtual pivot_rule rule() const = 0; + }; + + class first_eligible_pivot : public pivot_rule_impl { + edge_id m_next_edge; + public: + first_eligible_pivot(graph & g, vector & potentials, + svector & states, edge_id & enter_id) : + pivot_rule_impl(g, potentials, states, enter_id), + m_next_edge(0) { + } + virtual bool choose_entering_edge(); + virtual pivot_rule rule() const { return FIRST_ELIGIBLE; } + }; + + class best_eligible_pivot : public pivot_rule_impl { + public: + best_eligible_pivot(graph & g, vector & potentials, + svector & states, edge_id & enter_id) : + pivot_rule_impl(g, potentials, states, enter_id) { + } + virtual pivot_rule rule() const { return BEST_ELIGIBLE; } + virtual bool choose_entering_edge(); + }; + + class candidate_list_pivot : public pivot_rule_impl { + private: + edge_id m_next_edge; + svector m_candidates; + unsigned m_num_candidates; + unsigned m_minor_step; + unsigned m_current_length; + static const unsigned NUM_CANDIDATES = 10; + static const unsigned MINOR_STEP_LIMIT = 5; + + public: + candidate_list_pivot(graph & g, vector & potentials, + svector & states, edge_id & enter_id) : + pivot_rule_impl(g, potentials, states, enter_id), + m_next_edge(0), + m_minor_step(0), + m_current_length(0), + m_num_candidates(NUM_CANDIDATES), + m_candidates(m_num_candidates) { + } + + virtual pivot_rule rule() const { return CANDIDATE_LIST; } + + virtual bool choose_entering_edge(); + }; + + graph m_graph; + scoped_ptr m_tree; + scoped_ptr m_pivot; + vector m_balances; // nodes + 1 |-> [b -1b] Denote supply/demand b_i on node i + vector m_potentials; // nodes + 1 |-> initial: +/- 1 + // Duals of flows which are convenient to compute dual solutions + // become solutions to Dual simplex. + vector m_flows; // edges + nodes |-> assignemnt Basic feasible flows + svector m_states; + unsigned m_step; + edge_id m_enter_id; + edge_id m_leave_id; + optional m_delta; + + // Initialize the network with a feasible spanning tree + void initialize(); + + void update_potentials(); + + void update_flows(); + + bool choose_entering_edge(pivot_rule pr); + + // Send as much flow as possible around the cycle, the first basic edge with flow 0 will leave + // Return false if the problem is unbounded + bool choose_leaving_edge(); + + void update_spanning_tree(); + + numeral get_cost() const; + + bool edge_in_tree(edge_id id) const; + + bool is_infeasible(); + bool check_well_formed(); + bool check_optimal(); + + void display_primal(std::ofstream & os); + void display_dual(std::ofstream & os); + void display_spanning_tree(std::ofstream & os); + void display_system(std::ofstream & os); + + public: + + network_flow(graph & g, vector const & balances); + + // Minimize cost flows + // Return true if found an optimal solution, and return false if unbounded + min_flow_result min_cost(pivot_rule pr = FIRST_ELIGIBLE); + + // Compute the optimal solution + numeral get_optimal_solution(vector & result, bool is_dual); + + }; +} + +#endif diff --git a/src/math/simplex/network_flow_def.h b/src/math/simplex/network_flow_def.h new file mode 100644 index 000000000..a4a6246ce --- /dev/null +++ b/src/math/simplex/network_flow_def.h @@ -0,0 +1,526 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + network_flow_def.h + +Abstract: + + Implements Network Simplex algorithm for min cost flow problem + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-24 + +Notes: + +--*/ + +#ifndef NETWORK_FLOW_DEF_H_ +#define NETWORK_FLOW_DEF_H_ + +#include"network_flow.h" +#include"uint_set.h" +#include"spanning_tree_def.h" + +namespace smt { + + template + bool network_flow::first_eligible_pivot::choose_entering_edge() { + numeral cost = numeral::zero(); + int num_edges = m_graph.get_num_edges(); + for (int i = m_next_edge; i < m_next_edge + num_edges; ++i) { + edge_id id = i % num_edges; + if (edge_in_tree(id)) { + continue; + } + node src = m_graph.get_source(id); + node tgt = m_graph.get_target(id); + cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); + if (cost.is_pos()) { + m_next_edge = m_enter_id = id; + return true; + } + } + return false; + }; + + template + bool network_flow::best_eligible_pivot::choose_entering_edge() { + unsigned num_edges = m_graph.get_num_edges(); + numeral cost = numeral::zero(); + for (unsigned i = 0; i < num_edges; ++i) { + node src = m_graph.get_source(i); + node tgt = m_graph.get_target(i); + if (!edge_in_tree(i)) { + numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(i); + if (new_cost > cost) { + cost = new_cost; + m_enter_id = i; + } + } + } + return cost.is_pos(); + }; + + template + bool network_flow::candidate_list_pivot::choose_entering_edge() { + numeral cost = numeral::zero(); + if (m_current_length == 0 || m_minor_step == MINOR_STEP_LIMIT) { + // Build the candidate list + unsigned num_edges = m_graph.get_num_edges(); + m_current_length = 0; + for (unsigned i = m_next_edge; i < m_next_edge + num_edges; ++i) { + edge_id id = (i >= num_edges) ? i - num_edges : i; + node src = m_graph.get_source(id); + node tgt = m_graph.get_target(id); + if (!edge_in_tree(id)) { + numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); + if (new_cost.is_pos()) { + m_candidates[m_current_length] = id; + ++m_current_length; + if (new_cost > cost) { + cost = new_cost; + m_enter_id = id; + } + } + if (m_current_length >= m_num_candidates) break; + } + } + m_next_edge = m_enter_id; + m_minor_step = 1; + return cost.is_pos(); + } + + ++m_minor_step; + for (unsigned i = 0; i < m_current_length; ++i) { + edge_id id = m_candidates[i]; + node src = m_graph.get_source(id); + node tgt = m_graph.get_target(id); + if (!edge_in_tree(id)) { + numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id); + if (new_cost > cost) { + cost = new_cost; + m_enter_id = id; + } + // Remove stale candidates + if (!new_cost.is_pos()) { + --m_current_length; + m_candidates[i] = m_candidates[m_current_length]; + --i; + } + } + } + return cost.is_pos(); + }; + + template + network_flow::network_flow(graph & g, vector const & balances) : + m_balances(balances) { + // Network flow graph has the edges in the reversed order compared to constraint graph + // We only take enabled edges from the original graph + for (unsigned i = 0; i < g.get_num_nodes(); ++i) { + m_graph.init_var(i); + } + vector const & es = g.get_all_edges(); + for (unsigned i = 0; i < es.size(); ++i) { + edge const & e = es[i]; + if (e.is_enabled()) { + m_graph.add_edge(e.get_target(), e.get_source(), e.get_weight(), explanation()); + } + } + TRACE("network_flow", { + tout << "Difference logic optimization:" << std::endl; + display_dual(tout); + tout << "Minimum cost flow:" << std::endl; + display_primal(tout); + };); + + m_step = 0; + m_tree = alloc(basic_spanning_tree, m_graph); + } + + + template + void network_flow::initialize() { + TRACE("network_flow", tout << "initialize...\n";); + // Create an artificial root node to construct initial spanning tree + unsigned num_nodes = m_graph.get_num_nodes(); + unsigned num_edges = m_graph.get_num_edges(); + + node root = num_nodes; + m_graph.init_var(root); + + m_potentials.resize(num_nodes + 1); + m_potentials[root] = numeral::zero(); + + m_balances.resize(num_nodes + 1); + fin_numeral sum_supply = fin_numeral::zero(); + for (unsigned i = 0; i < num_nodes; ++i) { + sum_supply += m_balances[i]; + } + m_balances[root] = -sum_supply; + + m_flows.resize(num_nodes + num_edges); + m_flows.fill(numeral::zero()); + m_states.resize(num_nodes + num_edges); + m_states.fill(LOWER); + + // Create artificial edges from/to root node to/from other nodes and initialize the spanning tree + svector tree; + for (unsigned i = 0; i < num_nodes; ++i) { + bool is_forward = !m_balances[i].is_neg(); + m_states[num_edges + i] = BASIS; + node src = is_forward ? i : root; + node tgt = is_forward ? root : i; + m_flows[num_edges + i] = is_forward ? m_balances[i] : -m_balances[i]; + m_potentials[i] = is_forward ? numeral::one() : -numeral::one(); + tree.push_back(m_graph.add_edge(src, tgt, numeral::one(), explanation())); + } + + m_tree->initialize(tree); + + TRACE("network_flow", + tout << pp_vector("Potentials", m_potentials); + tout << pp_vector("Flows", m_flows); + tout << "Cost: " << get_cost() << "\n"; + tout << "Spanning tree:\n"; + display_spanning_tree(tout); + display_primal(tout);); + SASSERT(check_well_formed()); + } + + template + void network_flow::update_potentials() { + node src = m_graph.get_source(m_enter_id); + node tgt = m_graph.get_target(m_enter_id); + numeral cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(m_enter_id); + numeral change; + node start; + if (m_tree->in_subtree_t2(tgt)) { + change = cost; + start = tgt; + } + else { + change = -cost; + start = src; + } + SASSERT(m_tree->in_subtree_t2(start)); + TRACE("network_flow", tout << "update_potentials of T_" << start << " with change = " << change << "...\n";); + svector descendants; + m_tree->get_descendants(start, descendants); + SASSERT(descendants.size() >= 1); + for (unsigned i = 0; i < descendants.size(); ++i) { + node u = descendants[i]; + m_potentials[u] += change; + } + TRACE("network_flow", tout << pp_vector("Potentials", m_potentials);); + } + + template + void network_flow::update_flows() { + m_flows[m_enter_id] += *m_delta; + node src = m_graph.get_source(m_enter_id); + node tgt = m_graph.get_target(m_enter_id); + svector path; + svector against; + m_tree->get_path(src, tgt, path, against); + SASSERT(path.size() >= 1); + for (unsigned i = 0; i < path.size(); ++i) { + edge_id e_id = path[i]; + m_flows[e_id] += against[i] ? - *m_delta : *m_delta; + } + TRACE("network_flow", tout << pp_vector("Flows", m_flows);); + } + + template + bool network_flow::choose_leaving_edge() { + node src = m_graph.get_source(m_enter_id); + node tgt = m_graph.get_target(m_enter_id); + m_delta.set_invalid(); + edge_id leave_id = null_edge_id; + svector path; + svector against; + m_tree->get_path(src, tgt, path, against); + SASSERT(path.size() >= 1); + for (unsigned i = 0; i < path.size(); ++i) { + edge_id e_id = path[i]; + if (against[i] && (!m_delta || m_flows[e_id] < *m_delta)) { + m_delta = m_flows[e_id]; + leave_id = e_id; + } + } + m_leave_id = leave_id; + + return m_delta; + } + + template + void network_flow::update_spanning_tree() { + m_tree->update(m_enter_id, m_leave_id); + } + + template + bool network_flow::choose_entering_edge(pivot_rule pr) { + if (!m_pivot || pr != m_pivot->rule()) { + switch (pr) { + case FIRST_ELIGIBLE: + m_pivot = alloc(first_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id); + break; + case BEST_ELIGIBLE: + m_pivot = alloc(best_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id); + break; + case CANDIDATE_LIST: + m_pivot = alloc(candidate_list_pivot, m_graph, m_potentials, m_states, m_enter_id); + break; + default: + UNREACHABLE(); + } + } + return m_pivot->choose_entering_edge(); + } + + // Minimize cost flows + template + min_flow_result network_flow::min_cost(pivot_rule pr) { + initialize(); + while (choose_entering_edge(pr)) { + bool bounded = choose_leaving_edge(); + if (!bounded) return UNBOUNDED; + vectorconst& es = m_graph.get_all_edges(); + TRACE("network_flow", + { + edge const& e_in = es[m_enter_id]; + edge const& e_out = es[m_leave_id]; + node src_in = e_in.get_source(); + node tgt_in = e_in.get_target(); + node src_out = e_out.get_source(); + node tgt_out = e_out.get_target(); + numeral c1 = m_potentials[src_in] - m_potentials[tgt_in] - m_graph.get_weight(m_enter_id); + numeral c2 = m_potentials[src_out] - m_potentials[tgt_out] - m_graph.get_weight(m_leave_id); + tout << "new base: y_" << src_in << "_" << tgt_in << " cost: " << c1 << " delta: " << *m_delta << "\n"; + tout << "old base: y_" << src_out << "_" << tgt_out << " cost: " << c2 << "\n"; + } + ); + update_flows(); + if (m_enter_id != m_leave_id) { + SASSERT(edge_in_tree(m_leave_id)); + SASSERT(!edge_in_tree(m_enter_id)); + m_states[m_enter_id] = BASIS; + m_states[m_leave_id] = LOWER; + update_spanning_tree(); + update_potentials(); + TRACE("network_flow", + tout << "Spanning tree:\n"; + display_spanning_tree(tout); + tout << "Cost: " << get_cost() << "\n"; + display_primal(tout); + ); + SASSERT(check_well_formed()); + } + } + TRACE("network_flow", + tout << "Spanning tree:\n"; + display_spanning_tree(tout); + tout << "Cost: " << get_cost() << "\n"; + display_primal(tout); + ); + if (is_infeasible()) return INFEASIBLE; + TRACE("network_flow", tout << "Found optimal solution.\n";); + SASSERT(check_optimal()); + return OPTIMAL; + } + + template + bool network_flow::is_infeasible() { + // Flows of artificial arcs should be zero + unsigned num_nodes = m_graph.get_num_nodes(); + unsigned num_edges = m_graph.get_num_edges(); + SASSERT(m_flows.size() == num_edges); + for (unsigned i = 1; i < num_nodes; ++i) { + if (m_flows[num_edges - i].is_pos()) return true; + } + return false; + } + + // Get the optimal solution + template + typename network_flow::numeral network_flow::get_optimal_solution(vector & result, bool is_dual) { + numeral objective_value = get_cost(); + result.reset(); + if (is_dual) { + result.append(m_potentials); + } + else { + result.append(m_flows); + } + return objective_value; + } + + template + typename network_flow::numeral network_flow::get_cost() const { + numeral objective_value = numeral::zero(); + unsigned num_edges = m_graph.get_num_edges(); + for (unsigned i = 0; i < num_edges; ++i) { + if (edge_in_tree(i)) { + objective_value += m_flows[i].get_rational() * m_graph.get_weight(i); + } + } + return objective_value; + } + + template + bool network_flow::edge_in_tree(edge_id id) const { + return m_states[id] == BASIS; + } + + template + bool network_flow::check_well_formed() { + SASSERT(m_tree->check_well_formed()); + SASSERT(!m_delta || !(*m_delta).is_neg()); + + // m_flows are zero on non-basic edges + for (unsigned i = 0; i < m_flows.size(); ++i) { + SASSERT(!m_flows[i].is_neg()); + SASSERT(edge_in_tree(i) || m_flows[i].is_zero()); + } + + unsigned num_edges = m_graph.get_num_edges(); + for (unsigned i = 0; i < num_edges; ++i) { + if (edge_in_tree(i)) { + dl_var src = m_graph.get_source(i); + dl_var tgt = m_graph.get_target(i); + numeral weight = m_graph.get_weight(i); + SASSERT(m_potentials[src] - m_potentials[tgt] == weight); + } + } + + return true; + } + + template + bool network_flow::check_optimal() { + numeral total_cost = get_cost(); + unsigned num_edges = m_graph.get_num_edges(); + + for (unsigned i = 0; i < num_edges; ++i) { + dl_var src = m_graph.get_source(i); + dl_var tgt = m_graph.get_target(i); + numeral weight = m_graph.get_weight(i); + SASSERT(m_potentials[src] - m_potentials[tgt] <= weight); + } + + // m_flows are zero on non-basic edges + for (unsigned i = 0; i < m_flows.size(); ++i) { + SASSERT(edge_in_tree(i) || m_flows[i].is_zero()); + } + numeral total_balance = numeral::zero(); + for (unsigned i = 0; i < m_potentials.size(); ++i) { + total_balance += m_balances[i] * m_potentials[i]; + } + TRACE("network_flow", tout << "Total balance: " << total_balance << ", total cost: " << total_cost << std::endl;); + return total_cost == total_balance; + } + + // display methods + + template + void network_flow::display_primal(std::ofstream & os) { + vector const & es = m_graph.get_all_edges(); + for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { + edge const & e = es[i]; + os << "(declare-fun y_" << e.get_source() << "_" << e.get_target() << " () Real)" << std::endl; + }; + + for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { + edge const & e = es[i]; + os << "(assert (>= y_" << e.get_source() << "_" << e.get_target() << " 0))" << std::endl; + }; + + for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { + bool initialized = false; + for (unsigned j = 0; j < m_graph.get_num_edges(); ++j) { + edge const & e = es[j]; + if (e.get_target() == i || e.get_source() == i) { + if (!initialized) { + os << "(assert (= (+"; + } + initialized = true; + if (e.get_target() == i) { + os << " y_" << e.get_source() << "_" << e.get_target(); + } + else { + os << " (- y_" << e.get_source() << "_" << e.get_target() << ")"; + } + } + } + if(initialized) { + os << " " << m_balances[i] << ") 0))" << std::endl; + } + } + + os << "(minimize (+"; + for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { + edge const & e = es[i]; + os << " (* " << e.get_weight() << " y_" << e.get_source() << "_" << e.get_target() << ")"; + }; + os << "))" << std::endl; + os << "(optimize)" << std::endl; + if (!m_flows.empty()) { + for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { + edge const & e = es[i]; + os << "; y_" << e.get_source() << "_" << e.get_target() << " = " << m_flows[i] << "\n"; + } + } + } + + + template + void network_flow::display_dual(std::ofstream & os) { + for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { + os << "(declare-fun v" << i << " () Real)" << std::endl; + } + vector const & es = m_graph.get_all_edges(); + for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) { + edge const & e = es[i]; + os << "(assert (<= (- v" << e.get_source() << " v" << e.get_target() << ") " << e.get_weight() << "))" << std::endl; + }; + os << "(assert (= v0 0))" << std::endl; + os << "(maximize (+"; + for (unsigned i = 0; i < m_balances.size(); ++i) { + os << " (* " << m_balances[i] << " v" << i << ")"; + }; + os << "))" << std::endl; + os << "(optimize)" << std::endl; + } + + template + void network_flow::display_spanning_tree(std::ofstream & os) { + ++m_step;; + std::string prefix = "T"; + prefix.append(std::to_string(m_step)); + prefix.append("_"); + unsigned root = m_graph.get_num_nodes() - 1; + for (unsigned i = 0; i < root; ++i) { + os << prefix << i << "[shape=circle,label=\"" << prefix << i << " ["; + os << m_potentials[i] << "/" << m_balances[i] << "]\"];\n"; + } + os << prefix << root << "[shape=doublecircle,label=\"" << prefix << root << " ["; + os << m_potentials[root] << "/" << m_balances[root] << "]\"];\n"; + + unsigned num_edges = m_graph.get_num_edges(); + for (unsigned i = 0; i < num_edges; ++i) { + os << prefix << m_graph.get_source(i) << " -> " << prefix << m_graph.get_target(i); + if (edge_in_tree(i)) { + os << "[color=red,penwidth=3.0,label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n"; + } + else { + os << "[label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n"; + } + } + os << std::endl; + } +} + +#endif diff --git a/src/math/simplex/simplex.cpp b/src/math/simplex/simplex.cpp new file mode 100644 index 000000000..494c0b6bb --- /dev/null +++ b/src/math/simplex/simplex.cpp @@ -0,0 +1,26 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + simplex.h + +Abstract: + + Multi-precision simplex tableau. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-01-15 + +Notes: + +--*/ + +#include"simplex.h" +#include"sparse_matrix_def.h" +#include"simplex_def.h" +namespace simplex { + template class simplex; + template class simplex; +}; diff --git a/src/math/simplex/simplex.h b/src/math/simplex/simplex.h new file mode 100644 index 000000000..2d0fc4443 --- /dev/null +++ b/src/math/simplex/simplex.h @@ -0,0 +1,202 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + simplex.h + +Abstract: + + Multi-precision simplex tableau. + + - It uses code from theory_arith where applicable. + + - It is detached from the theory class and ASTs. + + - It uses non-shared mpz/mpq's avoiding global locks and operations on rationals. + + - It follows the same sparse tableau layout (no LU yet). + + - It does not include features for non-linear arithmetic. + + - Branch/bound/cuts is external. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-01-15 + +Notes: + +--*/ + +#ifndef SIMPLEX_H_ +#define SIMPLEX_H_ + +#include "sparse_matrix.h" +#include "mpq_inf.h" +#include "heap.h" +#include "lbool.h" +#include "uint_set.h" + +namespace simplex { + + template + class simplex { + + typedef unsigned var_t; + typedef typename Ext::eps_numeral eps_numeral; + typedef typename Ext::numeral numeral; + typedef typename Ext::manager manager; + typedef typename Ext::eps_manager eps_manager; + typedef typename Ext::scoped_numeral scoped_numeral; + typedef _scoped_numeral scoped_eps_numeral; + typedef _scoped_numeral_vector scoped_eps_numeral_vector; + typedef sparse_matrix matrix; + struct var_lt { + bool operator()(var_t v1, var_t v2) const { return v1 < v2; } + }; + typedef heap var_heap; + + struct stats { + unsigned m_num_pivots; + unsigned m_num_infeasible; + unsigned m_num_checks; + stats() { reset(); } + void reset() { + memset(this, 0, sizeof(*this)); + } + }; + + enum pivot_strategy_t { + S_BLAND, + S_GREATEST_ERROR, + S_LEAST_ERROR, + S_DEFAULT + }; + + struct var_info { + unsigned m_base2row:29; + unsigned m_is_base:1; + unsigned m_lower_valid:1; + unsigned m_upper_valid:1; + eps_numeral m_value; + eps_numeral m_lower; + eps_numeral m_upper; + numeral m_base_coeff; + var_info(): + m_base2row(0), + m_is_base(false), + m_lower_valid(false), + m_upper_valid(false) + {} + }; + + static const var_t null_var; + mutable manager m; + mutable eps_manager em; + mutable matrix M; + unsigned m_max_iterations; + volatile bool m_cancel; + var_heap m_to_patch; + vector m_vars; + svector m_row2base; + bool m_bland; + unsigned m_blands_rule_threshold; + random_gen m_random; + uint_set m_left_basis; + unsigned m_infeasible_var; + unsigned_vector m_base_vars; + stats m_stats; + + public: + simplex(): + M(m), + m_max_iterations(UINT_MAX), + m_cancel(false), + m_to_patch(1024), + m_bland(false), + m_blands_rule_threshold(1000) {} + + typedef typename matrix::row row; + typedef typename matrix::row_iterator row_iterator; + typedef typename matrix::col_iterator col_iterator; + + void ensure_var(var_t v); + row add_row(var_t base, unsigned num_vars, var_t const* vars, numeral const* coeffs); + row get_infeasible_row(); + var_t get_base_var(row const& r) const { return m_row2base[r.id()]; } + numeral const& get_base_coeff(row const& r) const { return m_vars[m_row2base[r.id()]].m_base_coeff; } + void del_row(var_t base_var); + void set_lower(var_t var, eps_numeral const& b); + void set_upper(var_t var, eps_numeral const& b); + void get_lower(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_lower; } + void get_upper(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_upper; } + bool above_lower(var_t var, eps_numeral const& b) const; + bool below_upper(var_t var, eps_numeral const& b) const; + bool below_lower(var_t v) const; + bool above_upper(var_t v) const; + bool lower_valid(var_t var) const { return m_vars[var].m_lower_valid; } + bool upper_valid(var_t var) const { return m_vars[var].m_upper_valid; } + void unset_lower(var_t var); + void unset_upper(var_t var); + void set_value(var_t var, eps_numeral const& b); + void set_cancel(bool f) { m_cancel = f; } + void set_max_iterations(unsigned n) { m_max_iterations = n; } + void reset(); + lbool make_feasible(); + lbool minimize(var_t var); + eps_numeral const& get_value(var_t v); + void display(std::ostream& out) const; + void display_row(std::ostream& out, row const& r, bool values = true); + + unsigned get_num_vars() const { return m_vars.size(); } + + row_iterator row_begin(row const& r) { return M.row_begin(r); } + row_iterator row_end(row const& r) { return M.row_end(r); } + + void collect_statistics(::statistics & st) const; + + private: + + void del_row(row const& r); + var_t select_var_to_fix(); + pivot_strategy_t pivot_strategy(); + var_t select_smallest_var() { return m_to_patch.empty()?null_var:m_to_patch.erase_min(); } + var_t select_error_var(bool least); + void check_blands_rule(var_t v, unsigned& num_repeated); + bool make_var_feasible(var_t x_i); + void update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value); + void update_value(var_t v, eps_numeral const& delta); + void update_value_core(var_t v, eps_numeral const& delta); + void pivot(var_t x_i, var_t x_j, numeral const& a_ij); + void move_to_bound(var_t x, bool to_lower); + var_t select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij); + var_t select_pivot_blands(var_t x_i, bool is_below, scoped_numeral& out_a_ij); + var_t select_pivot_core(var_t x_i, bool is_below, scoped_numeral& out_a_ij); + int get_num_non_free_dep_vars(var_t x_j, int best_so_far); + + var_t pick_var_to_leave(var_t x_j, bool is_pos, + scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc); + + + void select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j); + + + bool at_lower(var_t v) const; + bool at_upper(var_t v) const; + bool above_lower(var_t v) const; + bool below_upper(var_t v) const; + bool outside_bounds(var_t v) const { return below_lower(v) || above_upper(v); } + bool is_free(var_t v) const { return !m_vars[v].m_lower_valid && !m_vars[v].m_upper_valid; } + bool is_non_free(var_t v) const { return !is_free(v); } + bool is_base(var_t x) const { return m_vars[x].m_is_base; } + void add_patch(var_t v); + + bool well_formed() const; + bool well_formed_row(row const& r) const; + bool is_feasible() const; + }; + +}; + +#endif diff --git a/src/math/simplex/simplex_def.h b/src/math/simplex/simplex_def.h new file mode 100644 index 000000000..7a3ca01be --- /dev/null +++ b/src/math/simplex/simplex_def.h @@ -0,0 +1,1032 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + simplex_def.h + +Abstract: + +Author: + + Nikolaj Bjorner (nbjorner) 2014-01-15 + +Notes: + + Sign of base variables can vary. + Sign could possibly be normalized to positive. + Otherwise, sign could be accounted in pivoting. + +--*/ + +#ifndef SIMPLEX_DEF_H_ +#define SIMPLEX_DEF_H_ + + +namespace simplex { + + template + const typename simplex::var_t simplex::null_var = UINT_MAX; + + template + typename simplex::row + simplex::add_row(var_t base_var, unsigned num_vars, var_t const* vars, numeral const* coeffs) { + m_base_vars.reset(); + row r = M.mk_row(); + for (unsigned i = 0; i < num_vars; ++i) { + if (!m.is_zero(coeffs[i])) { + var_t v = vars[i]; + if (is_base(v)) { + m_base_vars.push_back(i); + } + M.add_var(r, coeffs[i], v); + } + } + scoped_numeral mul(m), a(m), b(m), c(m); + m.set(mul, 1); + for (unsigned i = 0; i < m_base_vars.size(); ++i) { + var_t v = vars[m_base_vars[i]]; + m.mul(coeffs[m_base_vars[i]], mul, a); + m.set(b, m_vars[v].m_base_coeff); + m.lcm(a, b, c); + TRACE("simplex", + m.display(tout << " a: ", a); + m.display(tout << " b v" << v << " : ", b); + m.display(tout << " c: ", c); + tout << "\n"; + M.display_row(tout, r); + M.display_row(tout, row(m_vars[v].m_base2row)); + if (m.is_zero(b)) { + display(tout); + }); + SASSERT(is_base(v)); + m.abs(c); + m.div(c, a, b); + m.div(c, m_vars[v].m_base_coeff, a); + m.mul(mul, b, mul); + M.mul(r, b); + m.neg(a); + M.add(r, a, row(m_vars[v].m_base2row)); + TRACE("simplex", M.display_row(tout, r);); + } + + scoped_numeral base_coeff(m); + scoped_eps_numeral value(em), tmp(em); + row_iterator it = M.row_begin(r), end = M.row_end(r); + for (; it != end; ++it) { + var_t v = it->m_var; + if (v == base_var) { + m.set(base_coeff, it->m_coeff); + } + else { + SASSERT(!is_base(v)); + em.mul(m_vars[v].m_value, it->m_coeff, tmp); + em.add(value, tmp, value); + } + } + SASSERT(!m.is_zero(base_coeff)); + TRACE("simplex", + for (unsigned i = 0; i < num_vars; ++i) { + m.display(tout << "v" << vars[i] << " * ", coeffs[i]); tout << " "; + if (i + 1 < num_vars) tout << " + "; + } + tout << "\n"; + row_iterator it2 = M.row_begin(r); + bool first = true; + for (; it2 != end; ++it2) { + if (!first) tout << " + "; + tout << "v" << it2->m_var << " * "; + m.display(tout, it2->m_coeff); tout << " "; + first = false; + } + tout << "\n"; + ); + SASSERT(!is_base(base_var)); + em.neg(value); + em.div(value, base_coeff, value); + while (m_row2base.size() <= r.id()) { + m_row2base.push_back(null_var); + } + m_row2base[r.id()] = base_var; + m_vars[base_var].m_base2row = r.id(); + m_vars[base_var].m_is_base = true; + m.set(m_vars[base_var].m_base_coeff, base_coeff); + em.set(m_vars[base_var].m_value, value); + add_patch(base_var); + SASSERT(well_formed_row(r)); + SASSERT(well_formed()); + return r; + } + + template + typename simplex::row + simplex::get_infeasible_row() { + SASSERT(is_base(m_infeasible_var)); + unsigned row_id = m_vars[m_infeasible_var].m_base2row; + return row(row_id); + } + + template + void simplex::add_patch(var_t v) { + SASSERT(is_base(v)); + if (outside_bounds(v)) { + TRACE("simplex", tout << "Add patch: v" << v << "\n";); + m_to_patch.insert(v); + } + } + + template + void simplex::del_row(row const& r) { + var_t var = m_row2base[r.id()]; + m_vars[var].m_is_base = false; + m_vars[var].m_lower_valid = false; + m_vars[var].m_upper_valid = false; + m_row2base[r.id()] = null_var; + M.del(r); + SASSERT(M.col_begin(var) == M.col_end(var)); + SASSERT(well_formed()); + } + + template + void simplex::del_row(var_t var) { + TRACE("simplex", tout << var << "\n";); + row r; + if (is_base(var)) { + r = row(m_vars[var].m_base2row); + } + else { + col_iterator it = M.col_begin(var), end = M.col_end(var); + if (it == end) { + return; + } + typename matrix::row_entry const& re = it.get_row_entry(); + r = it.get_row(); + var_t old_base = m_row2base[r.id()]; + scoped_eps_numeral new_value(em); + var_info& vi = m_vars[old_base]; + if (below_lower(old_base)) { + new_value = vi.m_lower; + } + else if (above_upper(old_base)) { + new_value = vi.m_upper; + } + else { + new_value = vi.m_value; + } + // need to move var such that old_base comes in bound. + update_and_pivot(old_base, var, re.m_coeff, new_value); + SASSERT(is_base(var)); + SASSERT(m_vars[var].m_base2row == r.id()); + SASSERT(!below_lower(old_base) && !above_upper(old_base)); + } + del_row(r); + TRACE("simplex", display(tout);); + SASSERT(well_formed()); + } + + template + bool simplex::above_lower(var_t var, eps_numeral const& b) const { + var_info const& vi = m_vars[var]; + return !vi.m_lower_valid || em.gt(b, vi.m_lower); + } + + template + bool simplex::below_upper(var_t var, eps_numeral const& b) const { + var_info const& vi = m_vars[var]; + return !vi.m_upper_valid || em.lt(b, vi.m_upper); + } + + template + void simplex::set_lower(var_t var, eps_numeral const& b) { + var_info& vi = m_vars[var]; + em.set(vi.m_lower, b); + vi.m_lower_valid = true; + TRACE("simplex", em.display(tout << "v" << var << " lower: ", b); + em.display(tout << " value: ", vi.m_value);); + SASSERT(!vi.m_upper_valid || em.le(b, vi.m_upper)); + if (!vi.m_is_base && em.lt(vi.m_value, b)) { + scoped_eps_numeral delta(em); + em.sub(b, vi.m_value, delta); + update_value(var, delta); + } + else if (vi.m_is_base && em.lt(vi.m_value, b)) { + SASSERT(outside_bounds(var)); + add_patch(var); + } + SASSERT(well_formed()); + } + + template + void simplex::set_upper(var_t var, eps_numeral const& b) { + var_info& vi = m_vars[var]; + em.set(vi.m_upper, b); + vi.m_upper_valid = true; + SASSERT(!vi.m_lower_valid || em.le(vi.m_lower, b)); + if (!vi.m_is_base && em.gt(vi.m_value, b)) { + scoped_eps_numeral delta(em); + em.sub(b, vi.m_value, delta); + update_value(var, delta); + } + else if (vi.m_is_base && em.lt(b, vi.m_value)) { + SASSERT(outside_bounds(var)); + add_patch(var); + } + SASSERT(well_formed()); + } + + template + void simplex::unset_lower(var_t var) { + m_vars[var].m_lower_valid = false; + } + + template + void simplex::unset_upper(var_t var) { + m_vars[var].m_upper_valid = false; + } + + template + void simplex::set_value(var_t var, eps_numeral const& b) { + scoped_eps_numeral delta(em); + em.sub(b, m_vars[var].m_value, delta); + update_value(var, delta); + SASSERT(well_formed()); + } + + template + typename simplex::eps_numeral const& + simplex::get_value(var_t v) { + return m_vars[v].m_value; + } + + template + void simplex::display(std::ostream& out) const { + M.display(out); + for (unsigned i = 0; i < m_vars.size(); ++i) { + var_info const& vi = m_vars[i]; + out << "v" << i << " "; + out << em.to_string(vi.m_value); + out << " ["; + if (vi.m_lower_valid) out << em.to_string(vi.m_lower); else out << "-oo"; + out << ":"; + if (vi.m_upper_valid) out << em.to_string(vi.m_upper); else out << "oo"; + out << "] "; + if (vi.m_is_base) out << "b:" << vi.m_base2row << " "; + //col_iterator it = M.col_begin(i), end = M.col_end(i); + //for (; it != end; ++it) { + // out << "r" << it.get_row().id() << " "; + //} + out << "\n"; + } + } + + template + void simplex::display_row(std::ostream& out, row const& r, bool values) { + row_iterator it = M.row_begin(r), end = M.row_end(r); + for (; it != end; ++it) { + m.display(out, it->m_coeff); + out << "*v" << it->m_var << " "; + if (values) { + var_info const& vi = m_vars[it->m_var]; + out << em.to_string(vi.m_value); + out << " ["; + if (vi.m_lower_valid) out << em.to_string(vi.m_lower); else out << "-oo"; + out << ":"; + if (vi.m_upper_valid) out << em.to_string(vi.m_upper); else out << "oo"; + out << "] "; + } + } + out << "\n"; + } + + + template + void simplex::ensure_var(var_t v) { + while (v >= m_vars.size()) { + M.ensure_var(m_vars.size()); + m_vars.push_back(var_info()); + } + if (m_to_patch.get_bounds() <= v) { + m_to_patch.set_bounds(2*v+1); + } + } + + template + void simplex::reset() { + M.reset(); + m_to_patch.reset(); + m_vars.reset(); + m_row2base.reset(); + m_left_basis.reset(); + m_base_vars.reset(); + } + + template + lbool simplex::make_feasible() { + ++m_stats.m_num_checks; + m_left_basis.reset(); + m_infeasible_var = null_var; + unsigned num_iterations = 0; + unsigned num_repeated = 0; + var_t v = null_var; + m_bland = false; + SASSERT(well_formed()); + while ((v = select_var_to_fix()) != null_var) { + TRACE("simplex", display(tout << "v" << v << "\n");); + if (m_cancel || num_iterations > m_max_iterations) { + return l_undef; + } + check_blands_rule(v, num_repeated); + if (!make_var_feasible(v)) { + m_to_patch.insert(v); + m_infeasible_var = v; + ++m_stats.m_num_infeasible; + return l_false; + } + ++num_iterations; + } + SASSERT(well_formed()); + return l_true; + } + + /** + \brief Make x_j the new base variable for row of x_i. + x_i is assumed base variable of row r_i. + x_j is assumed to have coefficient a_ij in r_i. + + a_ii*x_i + a_ij*x_j + r_i = 0 + + current value of x_i is v_i + new value of x_i is new_value + a_ii*(x_i + new_value - x_i) + a_ij*((x_i - new_value)*a_ii/a_ij + x_j) + r_i = 0 + + Let r_k be a row where x_j has coefficient x_kj != 0. + r_k <- r_k * a_ij - r_i * a_kj + */ + template + void simplex::update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value) { + SASSERT(is_base(x_i)); + SASSERT(!is_base(x_j)); + var_info& x_iI = m_vars[x_i]; + scoped_eps_numeral theta(em); + theta = x_iI.m_value; + theta -= new_value; + numeral const& a_ii = x_iI.m_base_coeff; + em.mul(theta, a_ii, theta); + em.div(theta, a_ij, theta); + update_value(x_j, theta); + SASSERT(em.eq(x_iI.m_value, new_value)); + pivot(x_i, x_j, a_ij); + } + + template + void simplex::pivot(var_t x_i, var_t x_j, numeral const& a_ij) { + ++m_stats.m_num_pivots; + var_info& x_iI = m_vars[x_i]; + var_info& x_jI = m_vars[x_j]; + unsigned r_i = x_iI.m_base2row; + m_row2base[r_i] = x_j; + x_jI.m_base2row = r_i; + m.set(x_jI.m_base_coeff, a_ij); + x_jI.m_is_base = true; + x_iI.m_is_base = false; + add_patch(x_j); + SASSERT(well_formed_row(row(r_i))); + + col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); + scoped_numeral a_kj(m), g(m); + for (; it != end; ++it) { + row r_k = it.get_row(); + if (r_k.id() != r_i) { + a_kj = it.get_row_entry().m_coeff; + a_kj.neg(); + M.mul(r_k, a_ij); + M.add(r_k, a_kj, row(r_i)); + var_t s = m_row2base[r_k.id()]; + numeral& coeff = m_vars[s].m_base_coeff; + m.mul(coeff, a_ij, coeff); + M.gcd_normalize(r_k, g); + if (!m.is_one(g)) { + m.div(coeff, g, coeff); + } + SASSERT(well_formed_row(row(r_k))); + } + } + SASSERT(well_formed()); + } + + template + void simplex::update_value(var_t v, eps_numeral const& delta) { + if (em.is_zero(delta)) { + return; + } + update_value_core(v, delta); + col_iterator it = M.col_begin(v), end = M.col_end(v); + + // v <- v + delta + // s*s_coeff + v*v_coeff + R = 0 + // -> + // (v + delta)*v_coeff + (s - delta*v_coeff/s_coeff)*v + R = 0 + for (; it != end; ++it) { + row r = it.get_row(); + var_t s = m_row2base[r.id()]; + var_info& si = m_vars[s]; + scoped_eps_numeral delta2(em); + numeral const& coeff = it.get_row_entry().m_coeff; + em.mul(delta, coeff, delta2); + em.div(delta2, si.m_base_coeff, delta2); + delta2.neg(); + update_value_core(s, delta2); + } + } + + template + void simplex::update_value_core(var_t v, eps_numeral const& delta) { + eps_numeral& val = m_vars[v].m_value; + em.add(val, delta, val); + if (is_base(v)) { + add_patch(v); + } + } + + template + bool simplex::below_lower(var_t v) const { + var_info const& vi = m_vars[v]; + return vi.m_lower_valid && em.lt(vi.m_value, vi.m_lower); + } + + + + template + bool simplex::above_upper(var_t v) const { + var_info const& vi = m_vars[v]; + return vi.m_upper_valid && em.gt(vi.m_value, vi.m_upper); + } + + template + bool simplex::above_lower(var_t v) const { + var_info const& vi = m_vars[v]; + return !vi.m_lower_valid || em.gt(vi.m_value, vi.m_lower); + } + + template + bool simplex::below_upper(var_t v) const { + var_info const& vi = m_vars[v]; + return !vi.m_upper_valid || em.lt(vi.m_value, vi.m_upper); + } + + template + bool simplex::at_lower(var_t v) const { + var_info const& vi = m_vars[v]; + return vi.m_lower_valid && em.eq(vi.m_value, vi.m_lower); + } + + template + bool simplex::at_upper(var_t v) const { + var_info const& vi = m_vars[v]; + return vi.m_upper_valid && em.eq(vi.m_value, vi.m_upper); + } + + template + bool simplex::make_var_feasible(var_t x_i) { + scoped_numeral a_ij(m); + scoped_eps_numeral value(em); + bool is_below; + if (below_lower(x_i)) { + SASSERT(is_base(x_i)); + is_below = m.is_pos(m_vars[x_i].m_base_coeff); + value = m_vars[x_i].m_lower; + } + else if (above_upper(x_i)) { + SASSERT(is_base(x_i)); + is_below = m.is_neg(m_vars[x_i].m_base_coeff); + value = m_vars[x_i].m_upper; + } + else { + // x_i is already feasible + return true; + } + var_t x_j = select_pivot(x_i, is_below, a_ij); + if (x_j != null_var) { + update_and_pivot(x_i, x_j, a_ij, value); + } + return x_j != null_var; + } + + /** + \brief Wrapper for select_pivot_blands and select_pivot_core + */ + template + typename simplex::var_t + simplex::select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij) { + if (m_bland) { + return select_pivot_blands(x_i, is_below, out_a_ij); + } + return select_pivot_core(x_i, is_below, out_a_ij); + } + + /** + \brief Select a variable x_j in the row r defining the base var x_i, + s.t. x_j can be used to patch the error in x_i. Return null_theory_var + if there is no x_j. Otherwise, return x_j and store its coefficient + in out_a_ij. + + The argument is_below is true (false) if x_i is below its lower + bound (above its upper bound). + */ + template + typename simplex::var_t + simplex::select_pivot_core(var_t x_i, bool is_below, scoped_numeral & out_a_ij) { + SASSERT(is_base(x_i)); + var_t max = get_num_vars(); + var_t result = max; + row r = row(m_vars[x_i].m_base2row); + int n; + unsigned best_col_sz = UINT_MAX; + int best_so_far = INT_MAX; + + row_iterator it = M.row_begin(r), end = M.row_end(r); + + for (; it != end; ++it) { + var_t x_j = it->m_var; + if (x_i == x_j) continue; + numeral const & a_ij = it->m_coeff; + + bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij); + bool is_pos = !is_neg; + bool can_pivot = ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j))); + if (can_pivot) { + int num = get_num_non_free_dep_vars(x_j, best_so_far); + unsigned col_sz = M.column_size(x_j); + if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) { + result = x_j; + out_a_ij = a_ij; + best_so_far = num; + best_col_sz = col_sz; + n = 1; + } + else if (num == best_so_far && col_sz == best_col_sz) { + n++; + if (m_random()%n == 0) { + result = x_j; + out_a_ij = a_ij; + } + } + } + } + return result < max ? result : null_var; + } + + /** + \brief Return the number of base variables that are non free and are v dependent. + The function adds 1 to the result if v is non free. + The function returns with a partial result r if r > best_so_far. + This function is used to select the pivot variable. + */ + template + int simplex::get_num_non_free_dep_vars(var_t x_j, int best_so_far) { + int result = is_non_free(x_j); + col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); + for (; it != end; ++it) { + var_t s = m_row2base[it.get_row().id()]; + result += is_non_free(s); + if (result > best_so_far) + return result; + } + return result; + } + + /** + \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, + s.t. x_j can be used to patch the error in x_i. Return null_var + if there is no x_j. Otherwise, return x_j and store its coefficient + in out_a_ij. + */ + template + typename simplex::var_t + simplex::select_pivot_blands(var_t x_i, bool is_below, scoped_numeral & out_a_ij) { + SASSERT(is_base(x_i)); + unsigned max = get_num_vars(); + var_t result = max; + row r(m_vars[x_i].m_base2row); + row_iterator it = M.row_begin(r), end = M.row_end(r); + for (; it != end; ++it) { + var_t x_j = it->m_var; + numeral const & a_ij = it->m_coeff; + bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij); + if (x_i != x_j && ((!is_neg && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { + SASSERT(!is_base(x_j)); + if (x_j < result) { + result = x_j; + out_a_ij = a_ij; + } + } + } + return result < max ? result : null_var; + } + + template + lbool simplex::minimize(var_t v) { + + // minimize v, such that tableau is feasible. + // Assume there are no bounds on v. + // Let k*v + c*x = 0 e.g, maximize c*x over + // tableau constraints: + // + // max { c*x | A*x = 0 and l <= x <= u } + // + // start with feasible assigment + // A*x0 = 0 and l <= x0 <= u + // + // Identify pivot: i, j: such that x_i is base, + // there is a row k1*x_i + k2*x_j + R = 0 + // and a delta such that: + // + // x_i' <- x_i + delta + // x_j' <- x_j - delta*k1/k2 + // l_i <= x_i' <= u_i + // l_j <= x_j' <= u_j + // and c*x' > c*x + // e.g., c*x := c_i*x_i + c_j*x_j + ... + // and c_i*delta > c_j*delta*k1/k2 + // and x_i < u_i (if delta > 0), l_i < x_i (if delta < 0) + // and l_j < x_j (if delta > 0), x_j < u_j (if delta < 0) + // + // update all rows, including c*x, using the pivot. + // + // If there is c_i*x_i in c*x such that c_i > 0 + // and upper_i = oo and complementary lower_j = -oo + // then the objective is unbounded. + // + // There is a singularity if there is a pivot such that + // c_i*delta == c_j*delta*k1/k2, e.g., nothing is improved, + // pivot, but use bland's rule to ensure + // convergence in the limit. + // + + SASSERT(is_feasible()); + scoped_eps_numeral delta(em); + scoped_numeral a_ij(m); + var_t x_i, x_j; + bool inc_x_i, inc_x_j; + + while (true) { + if (m_cancel) { + return l_undef; + } + select_pivot_primal(v, x_i, x_j, a_ij, inc_x_i, inc_x_j); + if (x_j == null_var) { + // optimal + return l_true; + } + TRACE("simplex", tout << "x_i: v" << x_i << " x_j: v" << x_j << "\n";); + var_info& vj = m_vars[x_j]; + if (x_i == null_var) { + if (inc_x_j && vj.m_upper_valid) { + delta = vj.m_upper; + delta -= vj.m_value; + update_value(x_j, delta); + } + else if (!inc_x_j && vj.m_lower_valid) { + delta = vj.m_lower; + delta -= vj.m_value; + update_value(x_j, delta); + } + else { + // unbounded + return l_false; + } + continue; + } + + // TBD: Change the value of x_j directly without pivoting: + // + // if (!vj.is_fixed() && vj.bounded() && gain >= upper - lower) { + // + // } + // + + pivot(x_i, x_j, a_ij); + TRACE("simplex", display(tout << "after pivot\n");); + move_to_bound(x_i, !inc_x_i); + SASSERT(well_formed_row(row(m_vars[x_j].m_base2row))); + TRACE("simplex", display(tout);); + SASSERT(is_feasible()); + } + return l_true; + } + + template + void simplex::move_to_bound(var_t x, bool to_lower) { + scoped_eps_numeral delta(em), delta2(em); + var_info& vi = m_vars[x]; + if (to_lower) { + em.sub(vi.m_value, vi.m_lower, delta); + } + else { + em.sub(vi.m_upper, vi.m_value, delta); + } + TRACE("simplex", tout << "move " << (to_lower?"to_lower":"to_upper") + << " v" << x << " delta: " << em.to_string(delta) << "\n";); + col_iterator it = M.col_begin(x), end = M.col_end(x); + for (; it != end && is_pos(delta); ++it) { + // + // base_coeff*s + coeff*x + R = 0 + // + // to_lower coeff > 0 base_coeff > 0 bound(s) + // ------------------------------------------------------ + // T T T !to_lower + // T T F to_lower + // T F T to_lower + // T F F !to_lower + // + var_t s = m_row2base[it.get_row().id()]; + var_info& vs = m_vars[s]; + numeral const& coeff = it.get_row_entry().m_coeff; + numeral const& base_coeff = vs.m_base_coeff; + SASSERT(!m.is_zero(coeff)); + bool base_to_lower = (m.is_pos(coeff) != m.is_pos(base_coeff)) == to_lower; + eps_numeral const* bound = 0; + if (!base_to_lower && vs.m_upper_valid) { + bound = &vs.m_upper; + } + else if (base_to_lower && vs.m_lower_valid) { + bound = &vs.m_lower; + } + if (bound) { + // |delta2*coeff| = |(bound-value)*base_coeff| + em.sub(*bound, vs.m_value, delta2); + em.mul(delta2, base_coeff, delta2); + em.div(delta2, coeff, delta2); + em.abs(delta2); + TRACE("simplex", tout << "Delta for v" << s << " " << delta2 << "\n";); + if (delta2 < delta) { + delta = delta2; + } + } + } + if (to_lower) { + delta.neg(); + } + update_value(x, delta); + } + + /** + \brief + Arguments: + v - base variable of row(v) to optimize + x_i - base variable of row(x_i) to become non-base + x_j - variable in row(v) to make a base variable + a_ij - coefficient to x_j in row(x_i) + inc - whether to increment x_i + */ + template + void simplex::select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, + bool& inc_x_i, bool& inc_x_j) { + row r(m_vars[v].m_base2row); + row_iterator it = M.row_begin(r), end = M.row_end(r); + + scoped_eps_numeral gain(em), new_gain(em); + scoped_numeral new_a_ij(m); + x_i = null_var; + x_j = null_var; + inc_x_i = false; + bool inc_y = false; + + for (; it != end; ++it) { + var_t x = it->m_var; + if (x == v) continue; + bool inc_x = m.is_pos(it->m_coeff) == m.is_pos(m_vars[v].m_base_coeff); + if ((inc_x && at_upper(x)) || (!inc_x && at_lower(x))) { + TRACE("simplex", tout << "v" << x << " pos: " << inc_x + << " at upper: " << at_upper(x) + << " at lower: " << at_lower(x) << "\n";); + continue; // variable cannot be used for improving bounds. + // TBD check? + } + var_t y = pick_var_to_leave(x, inc_x, new_gain, new_a_ij, inc_y); + if (y == null_var) { + // unbounded. + x_i = y; + x_j = x; + inc_x_i = inc_y; + inc_x_j = inc_x; + a_ij = new_a_ij; + break; + } + bool better = + (new_gain > gain) || + ((is_zero(new_gain) && is_zero(gain) && (x_i == null_var || y < x_i))); + + if (better) { + TRACE("simplex", + em.display(tout << "gain:", gain); + em.display(tout << " new gain:", new_gain); + tout << " base x_i: " << y << ", new base x_j: " << x << ", inc x_j: " << inc_x << "\n";); + + x_i = y; + x_j = x; + inc_x_i = inc_y; + inc_x_j = inc_x; + gain = new_gain; + a_ij = new_a_ij; + } + } + } + + // + // y is a base variable. + // v is a base variable. + // v*a_v + x*a_x + E = 0 + // y*b_y + x*b_x + F = 0 + // inc(x) := sign(a_v) == sign(a_x) + // sign_eq := sign(b_y) == sign(b_x) + // sign_eq => (inc(x) != inc(y)) + // !sign_eq => (inc(x) = inc(y)) + // -> + // inc(y) := sign_eq != inc(x) + // + + template + typename simplex::var_t + simplex::pick_var_to_leave( + var_t x_j, bool inc_x_j, + scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc_x_i) { + var_t x_i = null_var; + gain.reset(); + scoped_eps_numeral curr_gain(em); + col_iterator it = M.col_begin(x_j), end = M.col_end(x_j); + for (; it != end; ++it) { + row r = it.get_row(); + var_t s = m_row2base[r.id()]; + var_info& vi = m_vars[s]; + numeral const& a_ij = it.get_row_entry().m_coeff; + numeral const& a_ii = vi.m_base_coeff; + bool sign_eq = (m.is_pos(a_ii) == m.is_pos(a_ij)); + bool inc_s = sign_eq != inc_x_j; + TRACE("simplex", tout << "x_j: v" << x_j << ", base x_i: v" << s + << ", inc_x_i: " << inc_s + << ", inc_x_j: " << inc_x_j + << ", upper valid:" << vi.m_upper_valid + << ", lower valid:" << vi.m_lower_valid << "\n"; + display_row(tout, r);); + if ((inc_s && !vi.m_upper_valid) || (!inc_s && !vi.m_lower_valid)) { + continue; + } + // + // current gain: (value(x_i)-bound)*a_ii/a_ij + // + curr_gain = vi.m_value; + curr_gain -= inc_s?vi.m_upper:vi.m_lower; + em.mul(curr_gain, a_ii, curr_gain); + em.div(curr_gain, a_ij, curr_gain); + if (is_neg(curr_gain)) { + curr_gain.neg(); + } + if (x_i == null_var || (curr_gain < gain) || + (is_zero(gain) && is_zero(curr_gain) && s < x_i)) { + x_i = s; + gain = curr_gain; + new_a_ij = a_ij; + inc_x_i = inc_s; + TRACE("simplex", tout << "x_j v" << x_j << " x_i v" << x_i << " gain: "; + tout << curr_gain << "\n";); + } + } + return x_i; + } + + template + void simplex::check_blands_rule(var_t v, unsigned& num_repeated) { + if (m_bland) + return; + if (m_left_basis.contains(v)) { + num_repeated++; + if (num_repeated > m_blands_rule_threshold) { + TRACE("simplex", tout << "using blands rule, " << num_repeated << "\n";); + // std::cerr << "BLANDS RULE...\n"; + m_bland = true; + } + } + else { + m_left_basis.insert(v); + } + } + + + template + typename simplex::pivot_strategy_t + simplex::pivot_strategy() { + if (m_bland) { + return S_BLAND; + } + return S_DEFAULT; + } + + template + typename simplex::var_t + simplex::select_var_to_fix() { + switch (pivot_strategy()) { + case S_BLAND: + return select_smallest_var(); + case S_GREATEST_ERROR: + return select_error_var(false); + case S_LEAST_ERROR: + return select_error_var(true); + default: + return select_smallest_var(); + } + } + + template + typename simplex::var_t + simplex::select_error_var(bool least) { + var_t best = null_var; + scoped_eps_numeral best_error(em); + scoped_eps_numeral curr_error(em); + typename var_heap::iterator it = m_to_patch.begin(); + typename var_heap::iterator end = m_to_patch.end(); + for (; it != end; ++it) { + var_t v = *it; + var_info const& vi = m_vars[v]; + if (below_lower(v)) + em.sub(vi.m_lower, vi.m_value, curr_error); + else if (above_upper(v)) + em.sub(vi.m_value, vi.m_lower, curr_error); + else + continue; + SASSERT(is_pos(curr_error)); + if ((best == null_var) || + (!least && curr_error > best_error) || + (least && curr_error < best_error)) { + best = v; + best_error = curr_error; + } + } + if (best == null_var) + m_to_patch.clear(); // all variables are satisfied + else + m_to_patch.erase(best); + return best; + } + + template + bool simplex::well_formed() const { + SASSERT(M.well_formed()); + for (unsigned i = 0; i < m_row2base.size(); ++i) { + var_t s = m_row2base[i]; + if (s == null_var) continue; + SASSERT(i == m_vars[s].m_base2row); + VERIFY(well_formed_row(row(i))); + } + for (unsigned i = 0; i < m_vars.size(); ++i) { + if (!is_base(i)) { + SASSERT(!above_upper(i)); + SASSERT(!below_lower(i)); + } + } + return true; + } + + template + bool simplex::is_feasible() const { + for (unsigned i = 0; i < m_vars.size(); ++i) { + if (below_lower(i) || above_upper(i)) return false; + } + return true; + } + + template + bool simplex::well_formed_row(row const& r) const { + var_t s = m_row2base[r.id()]; + SASSERT(m_vars[s].m_base2row == r.id()); + SASSERT(m_vars[s].m_is_base); + // SASSERT(m.is_neg(m_vars[s].m_base_coeff)); + row_iterator it = M.row_begin(r), end = M.row_end(r); + scoped_eps_numeral sum(em), tmp(em); + for (; it != end; ++it) { + em.mul(m_vars[it->m_var].m_value, it->m_coeff, tmp); + sum += tmp; + SASSERT(s != it->m_var || m.eq(m_vars[s].m_base_coeff, it->m_coeff)); + } + if (!em.is_zero(sum)) { + IF_VERBOSE(0, M.display_row(verbose_stream(), r);); + TRACE("pb", display(tout << "non-well formed row\n"); M.display_row(tout << "row: ", r);); + throw default_exception("non-well formed row"); + } + + return true; + } + + template + void simplex::collect_statistics(::statistics & st) const { + M.collect_statistics(st); + st.update("simplex num pivots", m_stats.m_num_pivots); + st.update("simplex num infeasible", m_stats.m_num_infeasible); + st.update("simplex num checks", m_stats.m_num_checks); + } + + +}; + +#endif + diff --git a/src/math/simplex/sparse_matrix.h b/src/math/simplex/sparse_matrix.h new file mode 100644 index 000000000..803d27fde --- /dev/null +++ b/src/math/simplex/sparse_matrix.h @@ -0,0 +1,275 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sparse_matrix.h + +Abstract: + + +Author: + + Nikolaj Bjorner (nbjorner) 2014-01-15 + +Notes: + +--*/ + +#ifndef SPARSE_MATRIX_H_ +#define SPARSE_MATRIX_H_ + +#include "mpq_inf.h" +#include "statistics.h" + +namespace simplex { + + template + class sparse_matrix { + public: + typedef typename Ext::numeral numeral; + typedef typename Ext::scoped_numeral scoped_numeral; + typedef typename Ext::manager manager; + typedef unsigned var_t; + + struct row_entry { + numeral m_coeff; + var_t m_var; + row_entry(numeral const& c, var_t v): m_coeff(c), m_var(v) {} + }; + + private: + + struct stats { + unsigned m_add_rows; + stats() { reset(); } + void reset() { + memset(this, 0, sizeof(*this)); + } + }; + + static const unsigned dead_id = UINT_MAX; + + /** + \brief A row_entry is: m_var*m_coeff + + m_col_idx points to the place in the + column where the variable occurs. + */ + struct _row_entry : public row_entry { + union { + int m_col_idx; + int m_next_free_row_entry_idx; + }; + _row_entry(numeral const & c, var_t v): row_entry(c, v), m_col_idx(0) {} + _row_entry() : row_entry(numeral(), dead_id), m_col_idx(0) {} + bool is_dead() const { return row_entry::m_var == dead_id; } + }; + + /** + \brief A column entry points to the row and the row_entry within the row + that has a non-zero coefficient on the variable associated + with the column entry. + */ + struct col_entry { + int m_row_id; + union { + int m_row_idx; + int m_next_free_col_entry_idx; + }; + col_entry(int r, int i): m_row_id(r), m_row_idx(i) {} + col_entry(): m_row_id(0), m_row_idx(0) {} + bool is_dead() const { return (unsigned) m_row_id == dead_id; } + }; + + struct column; + + /** + \brief A row contains a base variable and set of + row_entries. The base variable must occur in the set of + row_entries with coefficient 1. + */ + struct _row { + vector<_row_entry> m_entries; + unsigned m_size; // the real size, m_entries contains dead row_entries. + int m_first_free_idx; // first available position. + _row(); + unsigned size() const { return m_size; } + unsigned num_entries() const { return m_entries.size(); } + void reset(manager& m); + _row_entry & add_row_entry(unsigned & pos_idx); + void del_row_entry(unsigned idx); + void compress(manager& m, vector & cols); + void compress_if_needed(manager& _m, vector & cols); + void save_var_pos(svector & result_map, unsigned_vector& idxs) const; + //bool is_coeff_of(var_t v, numeral const & expected) const; + int get_idx_of(var_t v) const; + }; + + /** + \brief A column stores in which rows a variable occurs. + The column may have free/dead entries. The field m_first_free_idx + is a reference to the first free/dead entry. + */ + struct column { + svector m_entries; + unsigned m_size; + int m_first_free_idx; + mutable unsigned m_refs; + + column():m_size(0), m_first_free_idx(-1), m_refs(0) {} + unsigned size() const { return m_size; } + unsigned num_entries() const { return m_entries.size(); } + void reset(); + void compress(vector<_row> & rows); + void compress_if_needed(vector<_row> & rows); + //void compress_singleton(vector<_row> & rows, unsigned singleton_pos); + col_entry const * get_first_col_entry() const; + col_entry & add_col_entry(int & pos_idx); + void del_col_entry(unsigned idx); + }; + + manager& m; + vector<_row> m_rows; + svector m_dead_rows; // rows to recycle + vector m_columns; // per var + svector m_var_pos; // temporary map from variables to positions in row + unsigned_vector m_var_pos_idx; // indices in m_var_pos + stats m_stats; + + bool well_formed_row(unsigned row_id) const; + bool well_formed_column(unsigned column_id) const; + void del_row_entry(_row& r, unsigned pos); + + public: + + sparse_matrix(manager& _m): m(_m) {} + ~sparse_matrix(); + void reset(); + + class row { + unsigned m_id; + public: + explicit row(unsigned r):m_id(r) {} + row():m_id(UINT_MAX) {} + bool operator!=(row const& other) const { + return m_id != other.m_id; + } + unsigned id() const { return m_id; } + }; + + void ensure_var(var_t v); + + row mk_row(); + void add_var(row r, numeral const& n, var_t var); + void add(row r, numeral const& n, row src); + void mul(row r, numeral const& n); + void neg(row r); + void del(row r); + + void gcd_normalize(row const& r, scoped_numeral& g); + + class row_iterator { + friend class sparse_matrix; + unsigned m_curr; + _row & m_row; + void move_to_used() { + while (m_curr < m_row.num_entries() && + m_row.m_entries[m_curr].is_dead()) { + ++m_curr; + } + } + row_iterator(_row & r, bool begin): + m_curr(0), m_row(r) { + if (begin) { + move_to_used(); + } + else { + m_curr = m_row.num_entries(); + } + } + public: + row_entry & operator*() const { return m_row.m_entries[m_curr]; } + row_entry * operator->() const { return &(operator*()); } + row_iterator & operator++() { ++m_curr; move_to_used(); return *this; } + row_iterator operator++(int) { row_iterator tmp = *this; ++*this; return tmp; } + bool operator==(row_iterator const & it) const { return m_curr == it.m_curr; } + bool operator!=(row_iterator const & it) const { return m_curr != it.m_curr; } + }; + + row_iterator row_begin(row const& r) { return row_iterator(m_rows[r.id()], true); } + row_iterator row_end(row const& r) { return row_iterator(m_rows[r.id()], false); } + + unsigned column_size(var_t v) const { return m_columns[v].size(); } + + class col_iterator { + friend class sparse_matrix; + unsigned m_curr; + column const& m_col; + vector<_row> const& m_rows; + void move_to_used() { + while (m_curr < m_col.num_entries() && m_col.m_entries[m_curr].is_dead()) { + ++m_curr; + } + } + col_iterator(column const& c, vector<_row> const& r, bool begin): + m_curr(0), m_col(c), m_rows(r) { + ++m_col.m_refs; + if (begin) { + move_to_used(); + } + else { + m_curr = m_col.num_entries(); + } + } + public: + ~col_iterator() { + --m_col.m_refs; + } + + row get_row() { + return row(m_col.m_entries[m_curr].m_row_id); + } + row_entry const& get_row_entry() { + col_entry const& c = m_col.m_entries[m_curr]; + int row_id = c.m_row_id; + return m_rows[row_id].m_entries[c.m_row_idx]; + } + + col_iterator & operator++() { ++m_curr; move_to_used(); return *this; } + col_iterator operator++(int) { col_iterator tmp = *this; ++*this; return tmp; } + bool operator==(col_iterator const & it) const { return m_curr == it.m_curr; } + bool operator!=(col_iterator const & it) const { return m_curr != it.m_curr; } + }; + + col_iterator col_begin(int v) const { return col_iterator(m_columns[v], m_rows, true); } + col_iterator col_end(int v) const { return col_iterator(m_columns[v], m_rows, false); } + + void display(std::ostream& out); + void display_row(std::ostream& out, row const& r); + bool well_formed() const; + + void collect_statistics(::statistics & st) const; + + }; + + struct mpz_ext { + typedef mpz numeral; + typedef scoped_mpz scoped_numeral; + typedef unsynch_mpz_manager manager; + typedef mpq_inf eps_numeral; + typedef unsynch_mpq_inf_manager eps_manager; + }; + + struct mpq_ext { + typedef mpq numeral; + typedef scoped_mpq scoped_numeral; + typedef unsynch_mpq_manager manager; + typedef mpq_inf eps_numeral; + typedef unsynch_mpq_inf_manager eps_manager; + }; + +}; + + +#endif diff --git a/src/math/simplex/sparse_matrix_def.h b/src/math/simplex/sparse_matrix_def.h new file mode 100644 index 000000000..09a8a9225 --- /dev/null +++ b/src/math/simplex/sparse_matrix_def.h @@ -0,0 +1,594 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sparse_matrix_def.h + +Abstract: + + +Author: + + Nikolaj Bjorner (nbjorner) 2014-01-15 + +Notes: + + mainly hoisted from theory_arith.h and theory_arith_aux.h + +--*/ + +#ifndef SPARSE_MATRIX_DEF_H_ +#define SPARSE_MATRIX_DEF_H_ + +#include "sparse_matrix.h" +#include "uint_set.h" + +namespace simplex { + + // ----------------------------------- + // + // Rows + // + // ----------------------------------- + + template + sparse_matrix::_row::_row(): + m_size(0), + m_first_free_idx(-1) { + } + + template + void sparse_matrix::_row::reset(manager& m) { + for (unsigned i = 0; i < m_entries.size(); ++i) { + m.reset(m_entries[i].m_coeff); + } + m_entries.reset(); + m_size = 0; + m_first_free_idx = -1; + } + + /** + \brief Add a new row_entry. The result is a reference to the new row_entry. + The position of the new row_entry in the + row is stored in pos_idx. + */ + template + typename sparse_matrix::_row_entry & + sparse_matrix::_row::add_row_entry(unsigned & pos_idx) { + m_size++; + if (m_first_free_idx == -1) { + pos_idx = m_entries.size(); + m_entries.push_back(_row_entry()); + return m_entries.back(); + } + else { + SASSERT(m_first_free_idx >= 0); + pos_idx = static_cast(m_first_free_idx); + _row_entry & result = m_entries[pos_idx]; + SASSERT(result.is_dead()); + m_first_free_idx = result.m_next_free_row_entry_idx; + return result; + } + } + + /** + \brief Delete row_entry at position idx. + */ + template + void sparse_matrix::_row::del_row_entry(unsigned idx) { + _row_entry & t = m_entries[idx]; + SASSERT(!t.is_dead()); + t.m_next_free_row_entry_idx = (unsigned)m_first_free_idx; + t.m_var = dead_id; + m_size--; + m_first_free_idx = idx; + SASSERT(t.is_dead()); + } + + /** + \brief Remove holes (i.e., dead entries) from the row. + */ + template + void sparse_matrix::_row::compress(manager& m, vector & cols) { + unsigned i = 0; + unsigned j = 0; + unsigned sz = m_entries.size(); + for (; i < sz; i++) { + _row_entry & t1 = m_entries[i]; + if (!t1.is_dead()) { + if (i != j) { + _row_entry & t2 = m_entries[j]; + t2.m_coeff.swap(t1.m_coeff); + t2.m_var = t1.m_var; + t2.m_col_idx = t1.m_col_idx; + SASSERT(!t2.is_dead()); + column & col = cols[t2.m_var]; + col.m_entries[t2.m_col_idx].m_row_idx = j; + } + j++; + } + } + SASSERT(j == m_size); + // + // alternative: update the free-list to point to the + // tail and avoid shrinking. + // if m.does not allocate memory (for wrapper around + // double), also bypass this step. + // + for (unsigned i = m_size; i < m_entries.size(); ++i) { + m.reset(m_entries[i].m_coeff); + } + m_entries.shrink(m_size); + m_first_free_idx = -1; + } + + template + void sparse_matrix::_row::compress_if_needed(manager& m, vector & cols) { + if (size() *2 < num_entries()) { + compress(m, cols); + } + } + + /** + \brief Fill the map var -> pos/idx + */ + template + inline void sparse_matrix::_row::save_var_pos(svector & result_map, unsigned_vector& idxs) const { + typename vector<_row_entry>::const_iterator it = m_entries.begin(); + typename vector<_row_entry>::const_iterator end = m_entries.end(); + unsigned idx = 0; + for (; it != end; ++it, ++idx) { + if (!it->is_dead()) { + result_map[it->m_var] = idx; + idxs.push_back(it->m_var); + } + } + } + + + template + int sparse_matrix::_row::get_idx_of(var_t v) const { + typename vector<_row_entry>::const_iterator it = m_entries.begin(); + typename vector<_row_entry>::const_iterator end = m_entries.end(); + for (unsigned idx = 0; it != end; ++it, ++idx) { + if (!it->is_dead() && it->m_var == v) + return idx; + } + return -1; + } + + // ----------------------------------- + // + // Columns + // + // ----------------------------------- + + template + void sparse_matrix::column::reset() { + m_entries.reset(); + m_size = 0; + m_first_free_idx = -1; + } + + /** + \brief Remove holes (i.e., dead entries) from the column. + */ + template + void sparse_matrix::column::compress(vector<_row> & rows) { + unsigned i = 0; + unsigned j = 0; + unsigned sz = m_entries.size(); + for (; i < sz; i++) { + col_entry & e1 = m_entries[i]; + if (!e1.is_dead()) { + if (i != j) { + m_entries[j] = e1; + _row & r = rows[e1.m_row_id]; + r.m_entries[e1.m_row_idx].m_col_idx = j; + } + j++; + } + } + SASSERT(j == m_size); + m_entries.shrink(m_size); + m_first_free_idx = -1; + } + + /** + \brief Invoke compress if the column contains too many holes (i.e., dead entries). + */ + template + inline void sparse_matrix::column::compress_if_needed(vector<_row> & rows) { + if (size() * 2 < num_entries() && m_refs == 0) { + compress(rows); + } + } + +#if 0 + /** + \brief Special version of compress, that is used when the column contain + only one entry located at position singleton_pos. + */ + template + void sparse_matrix::column::compress_singleton(vector<_row> & rows, unsigned singleton_pos) { + SASSERT(m_size == 1); + if (singleton_pos != 0) { + col_entry & s = m_entries[singleton_pos]; + m_entries[0] = s; + row & r = rows[s.m_row_id]; + r[s.m_row_idx].m_col_idx = 0; + } + m_first_free_idx = -1; + m_entries.shrink(1); + } +#endif + template + const typename sparse_matrix::col_entry * + sparse_matrix::column::get_first_col_entry() const { + typename svector::const_iterator it = m_entries.begin(); + typename svector::const_iterator end = m_entries.end(); + for (; it != end; ++it) { + if (!it->is_dead()) { + return it; + } + } + return 0; + } + + template + typename sparse_matrix::col_entry & + sparse_matrix::column::add_col_entry(int & pos_idx) { + m_size++; + if (m_first_free_idx == -1) { + pos_idx = m_entries.size(); + m_entries.push_back(col_entry()); + return m_entries.back(); + } + else { + pos_idx = m_first_free_idx; + col_entry & result = m_entries[pos_idx]; + SASSERT(result.is_dead()); + m_first_free_idx = result.m_next_free_col_entry_idx; + return result; + } + } + + template + void sparse_matrix::column::del_col_entry(unsigned idx) { + col_entry & c = m_entries[idx]; + SASSERT(!c.is_dead()); + c.m_row_id = dead_id; + c.m_next_free_col_entry_idx = m_first_free_idx; + m_first_free_idx = idx; + m_size--; + } + + // ----------------------------------- + // + // Matrix + // + // ----------------------------------- + + template + sparse_matrix::~sparse_matrix() { + for (unsigned i = 0; i < m_rows.size(); ++i) { + _row& r = m_rows[i]; + for (unsigned j = 0; j < r.m_entries.size(); ++j) { + m.reset(r.m_entries[j].m_coeff); + } + } + } + + template + void sparse_matrix::reset() { + m_rows.reset(); + m_dead_rows.reset(); + m_columns.reset(); + m_var_pos.reset(); + m_var_pos_idx.reset(); + + } + + template + void sparse_matrix::ensure_var(var_t v) { + while (m_columns.size() <= v) { + m_columns.push_back(column()); + m_var_pos.push_back(-1); + } + } + + template + typename sparse_matrix::row + sparse_matrix::mk_row() { + if (m_dead_rows.empty()) { + row r(m_rows.size()); + m_rows.push_back(_row()); + return r; + } + else { + row r(m_dead_rows.back()); + m_dead_rows.pop_back(); + return r; + } + } + + template + void sparse_matrix::add_var(row dst, numeral const& n, var_t v) { + _row& r = m_rows[dst.id()]; + column& c = m_columns[v]; + unsigned r_idx; + int c_idx; + _row_entry & r_entry = r.add_row_entry(r_idx); + col_entry& c_entry = c.add_col_entry(c_idx); + r_entry.m_var = v; + m.set(r_entry.m_coeff, n); + r_entry.m_col_idx = c_idx; + c_entry.m_row_id = dst.id(); + c_entry.m_row_idx = r_idx; + } + + /** + \brief Set row1 <- row1 + row2 * n + */ + template + void sparse_matrix::add(row row1, numeral const& n, row row2) { + m_stats.m_add_rows++; + _row & r1 = m_rows[row1.id()]; + + r1.save_var_pos(m_var_pos, m_var_pos_idx); + + // + // loop over variables in row2, + // add terms in row2 to row1. + // + +#define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \ + row_iterator it = row_begin(row2); \ + row_iterator end = row_end(row2); \ + for (; it != end; ++it) { \ + var_t v = it->m_var; \ + int pos = m_var_pos[v]; \ + if (pos == -1) { \ + /* variable v is not in row1 */ \ + unsigned row_idx; \ + _row_entry & r_entry = r1.add_row_entry(row_idx); \ + r_entry.m_var = v; \ + m.set(r_entry.m_coeff, it->m_coeff); \ + _SET_COEFF_; \ + column & c = m_columns[v]; \ + int col_idx; \ + col_entry & c_entry = c.add_col_entry(col_idx); \ + r_entry.m_col_idx = col_idx; \ + c_entry.m_row_id = row1.id(); \ + c_entry.m_row_idx = row_idx; \ + } \ + else { \ + /* variable v is in row1 */ \ + _row_entry & r_entry = r1.m_entries[pos]; \ + SASSERT(r_entry.m_var == v); \ + _ADD_COEFF_; \ + if (m.is_zero(r_entry.m_coeff)) { \ + del_row_entry(r1, pos); \ + } \ + } \ + } \ + ((void) 0) + + if (m.is_one(n)) { + ADD_ROW({}, + m.add(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff)); + } + else if (m.is_minus_one(n)) { + ADD_ROW(m.neg(r_entry.m_coeff), + m.sub(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff)); + } + else { + scoped_numeral tmp(m); + ADD_ROW(m.mul(r_entry.m_coeff, n, r_entry.m_coeff), + m.mul(it->m_coeff, n, tmp); + m.add(r_entry.m_coeff, tmp, r_entry.m_coeff)); + } + + // reset m_var_pos: + for (unsigned i = 0; i < m_var_pos_idx.size(); ++i) { + m_var_pos[m_var_pos_idx[i]] = -1; + } + m_var_pos_idx.reset(); + r1.compress_if_needed(m, m_columns); + } + + + template + void sparse_matrix::del_row_entry(_row& r, unsigned pos) { + _row_entry & r_entry = r.m_entries[pos]; + var_t v = r_entry.m_var; + int col_idx = r_entry.m_col_idx; + r.del_row_entry(pos); + column & c = m_columns[v]; + c.del_col_entry(col_idx); + c.compress_if_needed(m_rows); + } + + /** + \brief Set row <- -row + */ + template + void sparse_matrix::neg(row r) { + row_iterator it = row_begin(r); + row_iterator end = row_end(r); + for (; it != end; ++it) { + m.neg(it->m_coeff); + } + } + + /** + \brief Set row <- n*row + */ + template + void sparse_matrix::mul(row r, numeral const& n) { + SASSERT(!m.is_zero(n)); + if (m.is_one(n)) { + // no op + } + else if (m.is_minus_one(n)) { + neg(r); + } + else { + row_iterator it = row_begin(r); + row_iterator end = row_end(r); + for (; it != end; ++it) { + m.mul(it->m_coeff, n, it->m_coeff); + } + } + } + + /** + \brief Delete row. + */ + template + void sparse_matrix::del(row r) { + _row& rw = m_rows[r.id()]; + for (unsigned i = 0; i < rw.m_entries.size(); ++i) { + _row_entry& e = rw.m_entries[i]; + if (!e.is_dead()) { + del_row_entry(rw, i); + } + } + SASSERT(rw.m_size == 0); + m_dead_rows.push_back(r.id()); + } + + /** + \brief normalize coefficients by dividing with they coefficients. + return the gcd. + */ + template + void sparse_matrix::gcd_normalize(row const& r, scoped_numeral& g) { + g.reset(); + row_iterator it = row_begin(r), end = row_end(r); + for (; it != end && !m.is_one(g); ++it) { + if (m.is_zero(g)) g = it->m_coeff; + else m.gcd(g, it->m_coeff, g); + } + if (m.is_zero(g)) { + g = numeral(1); + } + if (!m.is_one(g)) { + row_iterator it2 = row_begin(r); + for (; it2 != end; ++it2) { + m.div(it2->m_coeff, g, it2->m_coeff); + } + } + } + + /** + \brief well_formed check + */ + template + bool sparse_matrix::well_formed() const { + for (unsigned i = 0; i < m_rows.size(); ++i) { + well_formed_row(i); + } + for (unsigned i = 0; i < m_columns.size(); ++i) { + well_formed_column(i); + } + return true; + } + + /** + \brief well_formed row check + */ + template + bool sparse_matrix::well_formed_row(unsigned row_id) const { + uint_set vars, dead; + _row const& r = m_rows[row_id]; + for (unsigned i = 0; i < r.num_entries(); ++i) { + _row_entry const& e = r.m_entries[i]; + if (e.is_dead()) { + dead.insert(i); + continue; + } + SASSERT(!vars.contains(e.m_var)); + SASSERT(!m.is_zero(e.m_coeff)); + SASSERT(e.m_var != dead_id); + col_entry const& c = m_columns[e.m_var].m_entries[e.m_col_idx]; + SASSERT((unsigned)c.m_row_id == row_id); + SASSERT((unsigned)c.m_row_idx == i); + vars.insert(e.m_var); + } + int idx = r.m_first_free_idx; + while (idx != -1) { + SASSERT(dead.contains(idx)); + dead.remove(idx); + idx = r.m_entries[idx].m_next_free_row_entry_idx; + } + SASSERT(dead.empty()); + return true; + } + + /** + \brief well_formed column check + */ + template + bool sparse_matrix::well_formed_column(var_t v) const { + uint_set rows, dead; + column const& col = m_columns[v]; + for (unsigned i = 0; i < col.num_entries(); ++i) { + col_entry const& c = col.m_entries[i]; + if (c.is_dead()) { + dead.insert(i); + continue; + } + SASSERT(!rows.contains(c.m_row_id)); + _row const& row = m_rows[c.m_row_id]; + _row_entry const& r = row.m_entries[c.m_row_idx]; + SASSERT(r.m_var == v); + SASSERT((unsigned)r.m_col_idx == i); + rows.insert(c.m_row_id); + } + int idx = col.m_first_free_idx; + while (idx != -1) { + SASSERT(dead.contains(idx)); + dead.remove(idx); + idx = col.m_entries[idx].m_next_free_col_entry_idx; + } + SASSERT(dead.empty()); + return true; + } + + /** + \brief statistics + */ + template + void sparse_matrix::collect_statistics(::statistics & st) const { + st.update("simplex add rows", m_stats.m_add_rows); + } + + + /** + \brief display method + */ + template + void sparse_matrix::display(std::ostream& out) { + for (unsigned i = 0; i < m_rows.size(); ++i) { + if (m_rows[i].size() == 0) continue; + display_row(out, row(i)); + } + } + + template + void sparse_matrix::display_row(std::ostream& out, row const& r) { + row_iterator it = row_begin(r), end = row_end(r); + for (; it != end; ++it) { + m.display(out, it->m_coeff); + out << "*v" << it->m_var << " "; + } + out << "\n"; + } + + + +}; + +#endif diff --git a/src/math/subpaving/subpaving.h b/src/math/subpaving/subpaving.h index ee6946fc5..d3db92741 100644 --- a/src/math/subpaving/subpaving.h +++ b/src/math/subpaving/subpaving.h @@ -22,8 +22,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_H_ -#define __SUBPAVING_H_ +#ifndef SUBPAVING_H_ +#define SUBPAVING_H_ #include"mpq.h" #include"subpaving_types.h" diff --git a/src/math/subpaving/subpaving_hwf.h b/src/math/subpaving/subpaving_hwf.h index a6b317a79..71cb03be4 100644 --- a/src/math/subpaving/subpaving_hwf.h +++ b/src/math/subpaving/subpaving_hwf.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_HWF_H_ -#define __SUBPAVING_HWF_H_ +#ifndef SUBPAVING_HWF_H_ +#define SUBPAVING_HWF_H_ #include"subpaving_t.h" #include"f2n.h" diff --git a/src/math/subpaving/subpaving_mpf.h b/src/math/subpaving/subpaving_mpf.h index 014a99f10..5cc11e6ab 100644 --- a/src/math/subpaving/subpaving_mpf.h +++ b/src/math/subpaving/subpaving_mpf.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_MPF_H_ -#define __SUBPAVING_MPF_H_ +#ifndef SUBPAVING_MPF_H_ +#define SUBPAVING_MPF_H_ #include"subpaving_t.h" #include"mpf.h" diff --git a/src/math/subpaving/subpaving_mpff.h b/src/math/subpaving/subpaving_mpff.h index f3353690a..c0ddb019d 100644 --- a/src/math/subpaving/subpaving_mpff.h +++ b/src/math/subpaving/subpaving_mpff.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_MPFF_H_ -#define __SUBPAVING_MPFF_H_ +#ifndef SUBPAVING_MPFF_H_ +#define SUBPAVING_MPFF_H_ #include"subpaving_t.h" #include"mpff.h" diff --git a/src/math/subpaving/subpaving_mpfx.h b/src/math/subpaving/subpaving_mpfx.h index 78e1d31f3..6213df1e5 100644 --- a/src/math/subpaving/subpaving_mpfx.h +++ b/src/math/subpaving/subpaving_mpfx.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_MPFX_H_ -#define __SUBPAVING_MPFX_H_ +#ifndef SUBPAVING_MPFX_H_ +#define SUBPAVING_MPFX_H_ #include"subpaving_t.h" #include"mpfx.h" diff --git a/src/math/subpaving/subpaving_mpq.h b/src/math/subpaving/subpaving_mpq.h index e3bd5b562..04bcd6059 100644 --- a/src/math/subpaving/subpaving_mpq.h +++ b/src/math/subpaving/subpaving_mpq.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_MPQ_H_ -#define __SUBPAVING_MPQ_H_ +#ifndef SUBPAVING_MPQ_H_ +#define SUBPAVING_MPQ_H_ #include"subpaving_t.h" #include"mpq.h" diff --git a/src/math/subpaving/subpaving_t.h b/src/math/subpaving/subpaving_t.h index 26a9802ad..a24199451 100644 --- a/src/math/subpaving/subpaving_t.h +++ b/src/math/subpaving/subpaving_t.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_T_H_ -#define __SUBPAVING_T_H_ +#ifndef SUBPAVING_T_H_ +#define SUBPAVING_T_H_ #include #include"tptr.h" diff --git a/src/math/subpaving/subpaving_types.h b/src/math/subpaving/subpaving_types.h index 8372b5b1f..e81b86e09 100644 --- a/src/math/subpaving/subpaving_types.h +++ b/src/math/subpaving/subpaving_types.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_TYPES_H_ -#define __SUBPAVING_TYPES_H_ +#ifndef SUBPAVING_TYPES_H_ +#define SUBPAVING_TYPES_H_ namespace subpaving { diff --git a/src/math/subpaving/tactic/expr2subpaving.h b/src/math/subpaving/tactic/expr2subpaving.h index 1deb023f7..7373ecf1e 100644 --- a/src/math/subpaving/tactic/expr2subpaving.h +++ b/src/math/subpaving/tactic/expr2subpaving.h @@ -17,8 +17,8 @@ Author: Notes: --*/ -#ifndef _EXPR2SUBPAVING_H_ -#define _EXPR2SUBPAVING_H_ +#ifndef EXPR2SUBPAVING_H_ +#define EXPR2SUBPAVING_H_ #include"ast.h" #include"subpaving.h" diff --git a/src/math/subpaving/tactic/subpaving_tactic.h b/src/math/subpaving/tactic/subpaving_tactic.h index 087c72847..30a72b888 100644 --- a/src/math/subpaving/tactic/subpaving_tactic.h +++ b/src/math/subpaving/tactic/subpaving_tactic.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef __SUBPAVING_TACTIC_H_ -#define __SUBPAVING_TACTIC_H_ +#ifndef SUBPAVING_TACTIC_H_ +#define SUBPAVING_TACTIC_H_ #include"params.h" class ast_manager; diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 9110014bf..fb594a09f 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -159,11 +159,17 @@ void func_interp::insert_entry(expr * const * args, expr * r) { void func_interp::insert_new_entry(expr * const * args, expr * r) { reset_interp_cache(); CTRACE("func_interp_bug", get_entry(args) != 0, + tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n"; + tout << "Args:"; + for (unsigned i = 0; i < m_arity; i++) { + tout << mk_ismt2_pp(get_entry(args)->get_arg(i), m_manager) << "\n"; + } + tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n"; + tout << "Args:"; for (unsigned i = 0; i < m_arity; i++) { tout << mk_ismt2_pp(args[i], m_manager) << "\n"; } - tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n"; - tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n";); + ); SASSERT(get_entry(args) == 0); func_entry * new_entry = func_entry::mk(m_manager, m_arity, args, r); if (!new_entry->args_are_values()) diff --git a/src/model/func_interp.h b/src/model/func_interp.h index fd21a6a7c..b264f4f1d 100644 --- a/src/model/func_interp.h +++ b/src/model/func_interp.h @@ -27,8 +27,8 @@ Author: Revision History: --*/ -#ifndef _FUNC_INTERP_H -#define _FUNC_INTERP_H +#ifndef FUNC_INTERP_H_ +#define FUNC_INTERP_H_ #include"ast.h" #include"ast_translation.h" diff --git a/src/model/model.h b/src/model/model.h index 70c8723fa..68ddc4015 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MODEL_H_ -#define _MODEL_H_ +#ifndef MODEL_H_ +#define MODEL_H_ #include"model_core.h" #include"ref.h" @@ -65,5 +65,5 @@ public: typedef ref model_ref; -#endif /* _MODEL_H_ */ +#endif /* MODEL_H_ */ diff --git a/src/model/model2expr.h b/src/model/model2expr.h index 3fd2e4524..272ba20b6 100644 --- a/src/model/model2expr.h +++ b/src/model/model2expr.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MODEL2EXPR_H_ -#define _MODEL2EXPR_H_ +#ifndef MODEL2EXPR_H_ +#define MODEL2EXPR_H_ #include"model.h" @@ -41,5 +41,5 @@ public: }; -#endif /* _MODEL2EXPR_H_ */ +#endif /* MODEL2EXPR_H_ */ diff --git a/src/model/model_core.h b/src/model/model_core.h index 72d7a321b..9925582f3 100644 --- a/src/model/model_core.h +++ b/src/model/model_core.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MODEL_CORE_H -#define _MODEL_CORE_H +#ifndef MODEL_CORE_H_ +#define MODEL_CORE_H_ #include"ast.h" #include"obj_hashtable.h" diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 02c43eaad..d649ac236 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -23,6 +23,7 @@ Revision History: #include"bool_rewriter.h" #include"arith_rewriter.h" #include"bv_rewriter.h" +#include"pb_rewriter.h" #include"datatype_rewriter.h" #include"array_rewriter.h" #include"fpa_rewriter.h" @@ -36,6 +37,7 @@ struct evaluator_cfg : public default_rewriter_cfg { bv_rewriter m_bv_rw; array_rewriter m_ar_rw; datatype_rewriter m_dt_rw; + pb_rewriter m_pb_rw; fpa_rewriter m_f_rw; unsigned long long m_max_memory; unsigned m_max_steps; @@ -52,6 +54,7 @@ struct evaluator_cfg : public default_rewriter_cfg { // See comment above. We want to allow customers to set :sort-store m_ar_rw(m, p), m_dt_rw(m), + m_pb_rw(m), m_f_rw(m) { m_b_rw.set_flat(false); m_a_rw.set_flat(false); @@ -153,6 +156,8 @@ struct evaluator_cfg : public default_rewriter_cfg { return m_ar_rw.mk_app_core(f, num, args, result); if (fid == m_dt_rw.get_fid()) return m_dt_rw.mk_app_core(f, num, args, result); + if (fid == m_pb_rw.get_fid()) + return m_pb_rw.mk_app_core(f, num, args, result); if (fid == m_f_rw.get_fid()) return m_f_rw.mk_app_core(f, num, args, result); return BR_FAILED; diff --git a/src/model/model_evaluator.h b/src/model/model_evaluator.h index 1a7469c6c..0884c1850 100644 --- a/src/model/model_evaluator.h +++ b/src/model/model_evaluator.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MODEL_EVALUATOR_H_ -#define _MODEL_EVALUATOR_H_ +#ifndef MODEL_EVALUATOR_H_ +#define MODEL_EVALUATOR_H_ #include"ast.h" #include"rewriter_types.h" diff --git a/src/model/model_implicant.cpp b/src/model/model_implicant.cpp new file mode 100644 index 000000000..44c70036c --- /dev/null +++ b/src/model/model_implicant.cpp @@ -0,0 +1,917 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + model_implicant.cpp + +Abstract: + + Facility to extract prime implicant from model. + +Author: + + Krystof Hoder (t-khoder) 2011-8-19. + +Revision History: + + +Notes: + + +--*/ + +#include +#include "array_decl_plugin.h" +#include "ast_pp.h" +#include "bool_rewriter.h" +#include "for_each_expr.h" +#include "model.h" +#include "ref_vector.h" +#include "rewriter.h" +#include "rewriter_def.h" +#include "util.h" +#include "model_implicant.h" +#include "arith_decl_plugin.h" +#include "expr_replacer.h" +#include "model_smt2_pp.h" +#include "poly_rewriter.h" +#include "poly_rewriter_def.h" +#include "arith_rewriter.h" +#include "scoped_proof.h" + + + +///////////////////////// +// model_implicant +// + + +void model_implicant::assign_value(expr* e, expr* val) { + rational r; + if (m.is_true(val)) { + set_true(e); + } + else if (m.is_false(val)) { + set_false(e); + } + else if (m_arith.is_numeral(val, r)) { + set_number(e, r); + } + else if (m.is_value(val)) { + set_value(e, val); + } + else { + IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); + TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); + set_x(e); + } +} + +void model_implicant::setup_model(model_ref& model) { + m_numbers.reset(); + m_values.reset(); + m_model = model; + rational r; + unsigned sz = model->get_num_constants(); + for (unsigned i = 0; i < sz; i++) { + func_decl * d = model->get_constant(i); + expr* val = model->get_const_interp(d); + expr* e = m.mk_const(d); + m_refs.push_back(e); + assign_value(e, val); + } +} + +void model_implicant::reset() { + m1.reset(); + m2.reset(); + m_values.reset(); + m_visited.reset(); + m_numbers.reset(); + m_refs.reset(); + m_model = 0; +} + +expr_ref_vector model_implicant::minimize_model(ptr_vector const & formulas, model_ref& mdl) { + setup_model(mdl); + + TRACE("pdr_verbose", + tout << "formulas:\n"; + for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; + ); + + expr_ref_vector model = prune_by_cone_of_influence(formulas); + TRACE("pdr_verbose", + tout << "pruned model:\n"; + for (unsigned i = 0; i < model.size(); ++i) tout << mk_pp(model[i].get(), m) << "\n";); + + reset(); + + DEBUG_CODE( + setup_model(mdl); + VERIFY(check_model(formulas)); + reset();); + + return model; +} + +expr_ref_vector model_implicant::minimize_literals(ptr_vector const& formulas, model_ref& mdl) { + + TRACE("pdr", + tout << "formulas:\n"; + for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; + ); + + expr_ref_vector result(m); + expr_ref tmp(m); + ptr_vector tocollect; + + setup_model(mdl); + collect(formulas, tocollect); + for (unsigned i = 0; i < tocollect.size(); ++i) { + expr* e = tocollect[i]; + expr* e1, *e2; + SASSERT(m.is_bool(e)); + SASSERT(is_true(e) || is_false(e)); + if (is_true(e)) { + result.push_back(e); + } + // hack to break disequalities for arithmetic variables. + else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) { + if (get_number(e1) < get_number(e2)) { + result.push_back(m_arith.mk_lt(e1,e2)); + } + else { + result.push_back(m_arith.mk_lt(e2,e1)); + } + } + else { + result.push_back(m.mk_not(e)); + } + } + reset(); + TRACE("pdr", + tout << "minimized model:\n"; + for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n"; + ); + + return result; +} + +void model_implicant::process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect) { + SASSERT(m.is_bool(e)); + SASSERT(is_true(e) || is_false(e)); + unsigned v = is_true(e); + unsigned sz = e->get_num_args(); + expr* const* args = e->get_args(); + if (e->get_family_id() == m.get_basic_family_id()) { + switch(e->get_decl_kind()) { + case OP_TRUE: + break; + case OP_FALSE: + break; + case OP_EQ: + case OP_IFF: + if (args[0] == args[1]) { + SASSERT(v); + // no-op + } + else if (m.is_bool(args[0])) { + todo.append(sz, args); + } + else { + tocollect.push_back(e); + } + break; + case OP_DISTINCT: + tocollect.push_back(e); + break; + case OP_ITE: + if (args[1] == args[2]) { + tocollect.push_back(args[1]); + } + else if (is_true(args[1]) && is_true(args[2])) { + todo.append(2, args+1); + } + else if (is_false(args[1]) && is_false(args[2])) { + todo.append(2, args+1); + } + else if (is_true(args[0])) { + todo.append(2, args); + } + else { + SASSERT(is_false(args[0])); + todo.push_back(args[0]); + todo.push_back(args[2]); + } + break; + case OP_AND: + if (v) { + todo.append(sz, args); + } + else { + unsigned i = 0; + for (; !is_false(args[i]) && i < sz; ++i); + if (i == sz) { + fatal_error(1); + } + VERIFY(i < sz); + todo.push_back(args[i]); + } + break; + case OP_OR: + if (v) { + unsigned i = 0; + for (; !is_true(args[i]) && i < sz; ++i); + if (i == sz) { + fatal_error(1); + } + VERIFY(i < sz); + todo.push_back(args[i]); + } + else { + todo.append(sz, args); + } + break; + case OP_XOR: + case OP_NOT: + todo.append(sz, args); + break; + case OP_IMPLIES: + if (v) { + if (is_true(args[1])) { + todo.push_back(args[1]); + } + else if (is_false(args[0])) { + todo.push_back(args[0]); + } + else { + IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); + UNREACHABLE(); + } + } + else { + todo.append(sz, args); + } + break; + default: + IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); + UNREACHABLE(); + } + } + else { + tocollect.push_back(e); + } +} + +void model_implicant::collect(ptr_vector const& formulas, ptr_vector& tocollect) { + ptr_vector todo; + todo.append(formulas); + m_visited.reset(); + + VERIFY(check_model(formulas)); + + while (!todo.empty()) { + app* e = to_app(todo.back()); + todo.pop_back(); + if (!m_visited.is_marked(e)) { + process_formula(e, todo, tocollect); + m_visited.mark(e, true); + } + } + m_visited.reset(); +} + +expr_ref_vector model_implicant::prune_by_cone_of_influence(ptr_vector const & formulas) { + ptr_vector tocollect; + collect(formulas, tocollect); + m1.reset(); + m2.reset(); + for (unsigned i = 0; i < tocollect.size(); ++i) { + TRACE("pdr_verbose", tout << "collect: " << mk_pp(tocollect[i], m) << "\n";); + for_each_expr(*this, m_visited, tocollect[i]); + } + unsigned sz = m_model->get_num_constants(); + expr_ref e(m), eq(m), val(m); + expr_ref_vector model(m); + for (unsigned i = 0; i < sz; i++) { + e = m.mk_const(m_model->get_constant(i)); + if (m_visited.is_marked(e)) { + val = eval(m_model, e); + eq = m.mk_eq(e, val); + model.push_back(eq); + } + } + m_visited.reset(); + TRACE("pdr", tout << sz << " ==> " << model.size() << "\n";); + return model; + +} + +void model_implicant::eval_arith(app* e) { + rational r, r2; + +#define ARG1 e->get_arg(0) +#define ARG2 e->get_arg(1) + + unsigned arity = e->get_num_args(); + for (unsigned i = 0; i < arity; ++i) { + expr* arg = e->get_arg(i); + if (is_x(arg)) { + set_x(e); + return; + } + SASSERT(!is_unknown(arg)); + } + switch(e->get_decl_kind()) { + case OP_NUM: + VERIFY(m_arith.is_numeral(e, r)); + set_number(e, r); + break; + case OP_IRRATIONAL_ALGEBRAIC_NUM: + set_x(e); + break; + case OP_LE: + set_bool(e, get_number(ARG1) <= get_number(ARG2)); + break; + case OP_GE: + set_bool(e, get_number(ARG1) >= get_number(ARG2)); + break; + case OP_LT: + set_bool(e, get_number(ARG1) < get_number(ARG2)); + break; + case OP_GT: + set_bool(e, get_number(ARG1) > get_number(ARG2)); + break; + case OP_ADD: + r = rational::zero(); + for (unsigned i = 0; i < arity; ++i) { + r += get_number(e->get_arg(i)); + } + set_number(e, r); + break; + case OP_SUB: + r = get_number(e->get_arg(0)); + for (unsigned i = 1; i < arity; ++i) { + r -= get_number(e->get_arg(i)); + } + set_number(e, r); + break; + case OP_UMINUS: + SASSERT(arity == 1); + set_number(e, get_number(e->get_arg(0))); + break; + case OP_MUL: + r = rational::one(); + for (unsigned i = 0; i < arity; ++i) { + r *= get_number(e->get_arg(i)); + } + set_number(e, r); + break; + case OP_DIV: + SASSERT(arity == 2); + r = get_number(ARG2); + if (r.is_zero()) { + set_x(e); + } + else { + set_number(e, get_number(ARG1) / r); + } + break; + case OP_IDIV: + SASSERT(arity == 2); + r = get_number(ARG2); + if (r.is_zero()) { + set_x(e); + } + else { + set_number(e, div(get_number(ARG1), r)); + } + break; + case OP_REM: + // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) + SASSERT(arity == 2); + r = get_number(ARG2); + if (r.is_zero()) { + set_x(e); + } + else { + r2 = mod(get_number(ARG1), r); + if (r.is_neg()) r2.neg(); + set_number(e, r2); + } + break; + case OP_MOD: + SASSERT(arity == 2); + r = get_number(ARG2); + if (r.is_zero()) { + set_x(e); + } + else { + set_number(e, mod(get_number(ARG1), r)); + } + break; + case OP_TO_REAL: + SASSERT(arity == 1); + set_number(e, get_number(ARG1)); + break; + case OP_TO_INT: + SASSERT(arity == 1); + set_number(e, floor(get_number(ARG1))); + break; + case OP_IS_INT: + SASSERT(arity == 1); + set_bool(e, get_number(ARG1).is_int()); + break; + case OP_POWER: + set_x(e); + break; + default: + IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); + UNREACHABLE(); + break; + } +} + +void model_implicant::inherit_value(expr* e, expr* v) { + expr* w; + SASSERT(!is_unknown(v)); + SASSERT(m.get_sort(e) == m.get_sort(v)); + if (is_x(v)) { + set_x(e); + } + else if (m.is_bool(e)) { + SASSERT(m.is_bool(v)); + if (is_true(v)) set_true(e); + else if (is_false(v)) set_false(e); + else { + TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); + set_x(e); + } + } + else if (m_arith.is_int_real(e)) { + set_number(e, get_number(v)); + } + else if (m.is_value(v)) { + set_value(e, v); + } + else if (m_values.find(v, w)) { + set_value(e, w); + } + else { + TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); + set_x(e); + } +} + +void model_implicant::eval_exprs(expr_ref_vector& es) { + model_ref mr(m_model); + for (unsigned j = 0; j < es.size(); ++j) { + if (m_array.is_as_array(es[j].get())) { + es[j] = eval(mr, es[j].get()); + } + } +} + +bool model_implicant::extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case) { + SASSERT(m_array.is_array(a)); + + TRACE("pdr", tout << mk_pp(a, m) << "\n";); + while (m_array.is_store(a)) { + expr_ref_vector store(m); + store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); + eval_exprs(store); + stores.push_back(store); + a = to_app(a)->get_arg(0); + } + + if (m_array.is_const(a)) { + else_case = to_app(a)->get_arg(0); + return true; + } + + while (m_array.is_as_array(a)) { + func_decl* f = m_array.get_as_array_func_decl(to_app(a)); + func_interp* g = m_model->get_func_interp(f); + unsigned sz = g->num_entries(); + unsigned arity = f->get_arity(); + for (unsigned i = 0; i < sz; ++i) { + expr_ref_vector store(m); + func_entry const* fe = g->get_entry(i); + store.append(arity, fe->get_args()); + store.push_back(fe->get_result()); + for (unsigned j = 0; j < store.size(); ++j) { + if (!is_ground(store[j].get())) { + TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); + return false; + } + } + eval_exprs(store); + stores.push_back(store); + } + else_case = g->get_else(); + if (!else_case) { + TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";); + return false; + } + if (!is_ground(else_case)) { + TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); + return false; + } + if (m_array.is_as_array(else_case)) { + model_ref mr(m_model); + else_case = eval(mr, else_case); + } + TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";); + return true; + } + TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";); + + return false; +} + +/** + best effort evaluator of extensional array equality. +*/ +void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) { + TRACE("pdr", tout << "array equality: " << mk_pp(e, m) << "\n";); + expr_ref v1(m), v2(m); + m_model->eval(arg1, v1); + m_model->eval(arg2, v2); + if (v1 == v2) { + set_true(e); + return; + } + sort* s = m.get_sort(arg1); + sort* r = get_array_range(s); + // give up evaluating finite domain/range arrays + if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { + TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); + set_x(e); + return; + } + vector store; + expr_ref else1(m), else2(m); + if (!extract_array_func_interp(v1, store, else1) || + !extract_array_func_interp(v2, store, else2)) { + TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); + set_x(e); + return; + } + + if (else1 != else2) { + if (m.is_value(else1) && m.is_value(else2)) { + TRACE("pdr", tout + << "defaults are different: " << mk_pp(e, m) << " " + << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); + set_false(e); + } + else if (m_array.is_array(else1)) { + eval_array_eq(e, else1, else2); + } + else { + TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); + set_x(e); + } + return; + } + + expr_ref s1(m), s2(m), w1(m), w2(m); + expr_ref_vector args1(m), args2(m); + args1.push_back(v1); + args2.push_back(v2); + for (unsigned i = 0; i < store.size(); ++i) { + args1.resize(1); + args2.resize(1); + args1.append(store[i].size()-1, store[i].c_ptr()); + args2.append(store[i].size()-1, store[i].c_ptr()); + s1 = m_array.mk_select(args1.size(), args1.c_ptr()); + s2 = m_array.mk_select(args2.size(), args2.c_ptr()); + m_model->eval(s1, w1); + m_model->eval(s2, w2); + if (w1 == w2) { + continue; + } + if (m.is_value(w1) && m.is_value(w2)) { + TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; + tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; + tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); + set_false(e); + } + else if (m_array.is_array(w1)) { + eval_array_eq(e, w1, w2); + if (is_true(e)) { + continue; + } + } + else { + TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); + set_x(e); + } + return; + } + set_true(e); +} + +void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) { + if (arg1 == arg2) { + set_true(e); + } + else if (m_array.is_array(arg1)) { + eval_array_eq(e, arg1, arg2); + } + else if (is_x(arg1) || is_x(arg2)) { + expr_ref eq(m), vl(m); + eq = m.mk_eq(arg1, arg2); + m_model->eval(eq, vl); + if (m.is_true(vl)) { + set_bool(e, true); + } + else if (m.is_false(vl)) { + set_bool(e, false); + } + else { + TRACE("pdr", tout << "cannot evaluate: " << mk_pp(vl, m) << "\n";); + set_x(e); + } + } + else if (m.is_bool(arg1)) { + bool val = is_true(arg1) == is_true(arg2); + SASSERT(val == (is_false(arg1) == is_false(arg2))); + if (val) { + set_true(e); + } + else { + set_false(e); + } + } + else if (m_arith.is_int_real(arg1)) { + set_bool(e, get_number(arg1) == get_number(arg2)); + } + else { + expr* e1 = get_value(arg1); + expr* e2 = get_value(arg2); + if (m.is_value(e1) && m.is_value(e2)) { + set_bool(e, e1 == e2); + } + else if (e1 == e2) { + set_bool(e, true); + } + else { + TRACE("pdr", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";); + set_x(e); + } + } +} + +void model_implicant::eval_basic(app* e) { + expr* arg1, *arg2; + expr *argCond, *argThen, *argElse, *arg; + bool has_x = false; + unsigned arity = e->get_num_args(); + switch(e->get_decl_kind()) { + case OP_AND: + for (unsigned j = 0; j < arity; ++j) { + expr * arg = e->get_arg(j); + if (is_false(arg)) { + set_false(e); + return; + } + else if (is_x(arg)) { + has_x = true; + } + else { + SASSERT(is_true(arg)); + } + } + if (has_x) { + set_x(e); + } + else { + set_true(e); + } + break; + case OP_OR: + for (unsigned j = 0; j < arity; ++j) { + expr * arg = e->get_arg(j); + if (is_true(arg)) { + set_true(e); + return; + } + else if (is_x(arg)) { + has_x = true; + } + else { + SASSERT(is_false(arg)); + } + } + if (has_x) { + set_x(e); + } + else { + set_false(e); + } + break; + case OP_NOT: + VERIFY(m.is_not(e, arg)); + if (is_true(arg)) { + set_false(e); + } + else if (is_false(arg)) { + set_true(e); + } + else { + SASSERT(is_x(arg)); + set_x(e); + } + break; + case OP_IMPLIES: + VERIFY(m.is_implies(e, arg1, arg2)); + if (is_false(arg1) || is_true(arg2)) { + set_true(e); + } + else if (arg1 == arg2) { + set_true(e); + } + else if (is_true(arg1) && is_false(arg2)) { + set_false(e); + } + else { + SASSERT(is_x(arg1) || is_x(arg2)); + set_x(e); + } + break; + case OP_IFF: + VERIFY(m.is_iff(e, arg1, arg2)); + eval_eq(e, arg1, arg2); + break; + case OP_ITE: + VERIFY(m.is_ite(e, argCond, argThen, argElse)); + if (is_true(argCond)) { + inherit_value(e, argThen); + } + else if (is_false(argCond)) { + inherit_value(e, argElse); + } + else if (argThen == argElse) { + inherit_value(e, argThen); + } + else if (m.is_bool(e)) { + SASSERT(is_x(argCond)); + if (is_x(argThen) || is_x(argElse)) { + set_x(e); + } + else if (is_true(argThen) == is_true(argElse)) { + inherit_value(e, argThen); + } + else { + set_x(e); + } + } + else { + set_x(e); + } + break; + case OP_TRUE: + set_true(e); + break; + case OP_FALSE: + set_false(e); + break; + case OP_EQ: + VERIFY(m.is_eq(e, arg1, arg2)); + eval_eq(e, arg1, arg2); + break; + case OP_DISTINCT: { + vector values; + for (unsigned i = 0; i < arity; ++i) { + expr* arg = e->get_arg(i); + if (is_x(arg)) { + set_x(e); + return; + } + values.push_back(get_number(arg)); + } + std::sort(values.begin(), values.end()); + for (unsigned i = 0; i + 1 < values.size(); ++i) { + if (values[i] == values[i+1]) { + set_false(e); + return; + } + } + set_true(e); + break; + } + default: + IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); + UNREACHABLE(); + } +} + +bool model_implicant::check_model(ptr_vector const& formulas) { + ptr_vector todo(formulas); + + while (!todo.empty()) { + expr * curr_e = todo.back(); + + if (!is_app(curr_e)) { + todo.pop_back(); + continue; + } + app * curr = to_app(curr_e); + + if (!is_unknown(curr)) { + todo.pop_back(); + continue; + } + unsigned arity = curr->get_num_args(); + for (unsigned i = 0; i < arity; ++i) { + if (is_unknown(curr->get_arg(i))) { + todo.push_back(curr->get_arg(i)); + } + } + if (todo.back() != curr) { + continue; + } + todo.pop_back(); + if (curr->get_family_id() == m_arith.get_family_id()) { + eval_arith(curr); + } + else if (curr->get_family_id() == m.get_basic_family_id()) { + eval_basic(curr); + } + else { + expr_ref vl(m); + m_model->eval(curr, vl); + assign_value(curr, vl); + } + + IF_VERBOSE(35,verbose_stream() << "assigned "<get_arity() == 0); + expr_ref result(m); + if (m_array.is_array(d->get_range())) { + expr_ref e(m); + e = m.mk_const(d); + result = eval(model, e); + } + else { + result = model->get_const_interp(d); + } + return result; +} + +expr_ref model_implicant::eval(model_ref& model, expr* e) { + expr_ref result(m); + m_model = model; + VERIFY(m_model->eval(e, result, true)); + if (m_array.is_array(e)) { + vector stores; + expr_ref_vector args(m); + expr_ref else_case(m); + if (extract_array_func_interp(result, stores, else_case)) { + result = m_array.mk_const_array(m.get_sort(e), else_case); + while (!stores.empty() && stores.back().back() == else_case) { + stores.pop_back(); + } + for (unsigned i = stores.size(); i > 0; ) { + --i; + args.resize(1); + args[0] = result; + args.append(stores[i]); + result = m_array.mk_store(args.size(), args.c_ptr()); + } + return result; + } + } + return result; +} + + + + diff --git a/src/model/model_implicant.h b/src/model/model_implicant.h new file mode 100644 index 000000000..91bfe9894 --- /dev/null +++ b/src/model/model_implicant.h @@ -0,0 +1,118 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + model_implicant.h + +Abstract: + + Facility to extract prime implicant from model. + +Author: + + Krystof Hoder (t-khoder) 2011-8-19. + +Revision History: + +--*/ + +#ifndef MODEL_IMPLICANT_H_ +#define MODEL_IMPLICANT_H_ + +#include "ast.h" +#include "ast_pp.h" +#include "obj_hashtable.h" +#include "ref_vector.h" +#include "trace.h" +#include "vector.h" +#include "arith_decl_plugin.h" +#include "array_decl_plugin.h" + +class model; +class model_core; + +class model_implicant { + ast_manager& m; + arith_util m_arith; + array_util m_array; + obj_map m_numbers; + expr_ref_vector m_refs; + obj_map m_values; + model_ref m_model; + + //00 -- non-visited + //01 -- X + //10 -- false + //11 -- true + expr_mark m1; + expr_mark m2; + expr_mark m_visited; + + + void reset(); + void setup_model(model_ref& model); + void assign_value(expr* e, expr* v); + void collect(ptr_vector const& formulas, ptr_vector& tocollect); + void process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect); + expr_ref_vector prune_by_cone_of_influence(ptr_vector const & formulas); + void eval_arith(app* e); + void eval_basic(app* e); + void eval_eq(app* e, expr* arg1, expr* arg2); + void eval_array_eq(app* e, expr* arg1, expr* arg2); + void inherit_value(expr* e, expr* v); + + inline bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); } + inline void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); } + inline bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); } + inline bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); } + inline bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); } + inline void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); } + inline void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } + inline void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } + inline void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); } + inline void set_bool(expr* x, bool v) { if (v) { set_true(x); } else { set_false(x); } } + inline rational const& get_number(expr* x) const { return m_numbers.find(x); } + inline void set_number(expr* x, rational const& v) { + set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_numbers.insert(x,v); + } + inline expr* get_value(expr* x) { return m_values.find(x); } + inline void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); } + + bool check_model(ptr_vector const & formulas); + + bool extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case); + + void eval_exprs(expr_ref_vector& es); + +public: + model_implicant(ast_manager& m) : + m(m), m_arith(m), m_array(m), m_refs(m) {} + + /** + \brief extract equalities from model that suffice to satisfy formula. + + \pre model satisfies formulas + */ + + expr_ref_vector minimize_model(ptr_vector const & formulas, model_ref& mdl); + + /** + \brief extract literals from model that satisfy formulas. + + \pre model satisfies formulas + */ + expr_ref_vector minimize_literals(ptr_vector const & formulas, model_ref& mdl); + + /** + for_each_expr visitor. + */ + void operator()(expr* e) {} + + expr_ref eval(model_ref& mdl, expr* e); + + expr_ref eval(model_ref& mdl, func_decl* d); +}; + + +#endif diff --git a/src/model/model_pp.h b/src/model/model_pp.h index 31a6083a7..1b18d9a88 100644 --- a/src/model/model_pp.h +++ b/src/model/model_pp.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _MODEL_PP_H_ -#define _MODEL_PP_H_ +#ifndef MODEL_PP_H_ +#define MODEL_PP_H_ #include class model_core; diff --git a/src/model/model_smt2_pp.h b/src/model/model_smt2_pp.h index 0b76a8bed..c123bfb24 100644 --- a/src/model/model_smt2_pp.h +++ b/src/model/model_smt2_pp.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _MODEL_SMT2_PP_H_ -#define _MODEL_SMT2_PP_H_ +#ifndef MODEL_SMT2_PP_H_ +#define MODEL_SMT2_PP_H_ #include"ast_printer.h" #include"model_core.h" diff --git a/src/model/model_v2_pp.h b/src/model/model_v2_pp.h index 9121f023a..6715d055a 100644 --- a/src/model/model_v2_pp.h +++ b/src/model/model_v2_pp.h @@ -15,8 +15,8 @@ Author: Revision History: --*/ -#ifndef _MODEL_V2_PP_H_ -#define _MODEL_V2_PP_H_ +#ifndef MODEL_V2_PP_H_ +#define MODEL_V2_PP_H_ #include class model_core; diff --git a/src/muz/base/bind_variables.cpp b/src/muz/base/bind_variables.cpp new file mode 100644 index 000000000..0cc50625e --- /dev/null +++ b/src/muz/base/bind_variables.cpp @@ -0,0 +1,154 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + bind_variables.cpp + +Abstract: + + Utility to find constants that are declared as variables. + +Author: + + Nikolaj Bjorner (nbjorner) 9-24-2014 + +Notes: + + +--*/ + +#include "bind_variables.h" + +bind_variables::bind_variables(ast_manager & m): + m(m), + m_vars(m), + m_pinned(m) +{} + +bind_variables::~bind_variables() { +} + +expr_ref bind_variables::operator()(expr* fml, bool is_forall) { + if (m_vars.empty()) { + return expr_ref(fml, m); + } + SASSERT(m_pinned.empty()); + expr_ref result = abstract(fml, m_cache, 0); + if (!m_names.empty()) { + m_bound.reverse(); + m_names.reverse(); + result = m.mk_quantifier(is_forall, m_bound.size(), m_bound.c_ptr(), m_names.c_ptr(), result); + } + m_pinned.reset(); + m_cache.reset(); + m_names.reset(); + m_bound.reset(); + for (var2bound::iterator it = m_var2bound.begin(); it != m_var2bound.end(); ++it) { + it->m_value = 0; + } + return result; +} + + +expr_ref bind_variables::abstract(expr* term, cache_t& cache, unsigned scope) { + unsigned sz = m_todo.size(); + m_todo.push_back(term); + m_args.reset(); + expr* b, *arg; + while (m_todo.size() > sz) { + expr* e = m_todo.back(); + if (cache.contains(e)) { + m_todo.pop_back(); + continue; + } + switch(e->get_kind()) { + case AST_VAR: { + SASSERT(to_var(e)->get_idx() < scope); + // mixing bound variables and free is possible for the caller, + // but not proper use. + // So we assert here even though we don't check for it. + cache.insert(e, e); + m_todo.pop_back(); + break; + } + case AST_APP: { + app* a = to_app(e); + var2bound::obj_map_entry* w = m_var2bound.find_core(a); + if (w) { + var* v = w->get_data().m_value; + if (!v) { + // allocate a bound index. + v = m.mk_var(m_names.size(), m.get_sort(a)); + m_names.push_back(a->get_decl()->get_name()); + m_bound.push_back(m.get_sort(a)); + w->get_data().m_value = v; + m_pinned.push_back(v); + } + if (scope == 0) { + cache.insert(e, v); + } + else { + var* v1 = m.mk_var(scope + v->get_idx(), m.get_sort(v)); + m_pinned.push_back(v1); + cache.insert(e, v1); + } + m_todo.pop_back(); + break; + } + bool all_visited = true; + bool some_diff = false; + m_args.reset(); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + arg = a->get_arg(i); + if (!cache.find(arg, b)) { + m_todo.push_back(arg); + all_visited = false; + } + else if (all_visited) { + m_args.push_back(b); + if (b != arg) { + some_diff = true; + } + } + } + if (all_visited) { + if (some_diff) { + b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); + m_pinned.push_back(b); + } + else { + b = a; + } + cache.insert(e, b); + m_todo.pop_back(); + } + break; + } + case AST_QUANTIFIER: { + quantifier* q = to_quantifier(e); + expr_ref_buffer patterns(m); + expr_ref result1(m); + unsigned new_scope = scope + q->get_num_decls(); + cache_t new_cache; + for (unsigned i = 0; i < q->get_num_patterns(); ++i) { + patterns.push_back(abstract(q->get_pattern(i), new_cache, new_scope)); + } + result1 = abstract(q->get_expr(), new_cache, new_scope); + b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get()); + m_pinned.push_back(b); + cache.insert(e, b); + m_todo.pop_back(); + break; + } + default: + UNREACHABLE(); + } + } + return expr_ref(cache.find(term), m); +} + +void bind_variables::add_var(app* v) { + m_vars.push_back(v); + m_var2bound.insert(v, 0); +} diff --git a/src/muz/base/bind_variables.h b/src/muz/base/bind_variables.h new file mode 100644 index 000000000..f2aa29224 --- /dev/null +++ b/src/muz/base/bind_variables.h @@ -0,0 +1,51 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + bind_variables.h + +Abstract: + + Utility to find constants that are declard as varaibles. + +Author: + + Nikolaj Bjorner (nbjorner) 9-24-2014 + +Notes: + + +--*/ + +#ifndef BIND_VARIABLES_H_ +#define BIND_VARIABLES_H_ + +#include"ast.h" + +class bind_variables { + typedef obj_map var2bound; + typedef obj_map cache_t; + ast_manager& m; + app_ref_vector m_vars; + obj_map m_cache; + var2bound m_var2bound; + expr_ref_vector m_pinned; + ptr_vector m_bound; + svector m_names; + ptr_vector m_todo; + ptr_vector m_args; + + + + expr_ref abstract(expr* fml, cache_t& cache, unsigned scope); +public: + bind_variables(ast_manager & m); + ~bind_variables(); + + expr_ref operator()(expr* fml, bool is_forall); + + void add_var(app* v); +}; + +#endif /* BIND_VARIABLES_H_ */ diff --git a/src/muz/base/dl_boogie_proof.cpp b/src/muz/base/dl_boogie_proof.cpp index 42d21dfb9..75579323c 100644 --- a/src/muz/base/dl_boogie_proof.cpp +++ b/src/muz/base/dl_boogie_proof.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + /** Example from Boogie: @@ -49,7 +55,7 @@ Example from Boogie: #include "model_pp.h" #include "proof_utils.h" #include "ast_pp.h" -#include "qe_util.h" +#include "ast_util.h" namespace datalog { @@ -91,7 +97,7 @@ namespace datalog { if (!m.is_implies(premise, l1, l2)) { continue; } - qe::flatten_and(l1, literals); + flatten_and(l1, literals); positions2.reset(); premises2.reset(); premises2.push_back(premise); diff --git a/src/muz/base/dl_boogie_proof.h b/src/muz/base/dl_boogie_proof.h index 6c8fbbae3..0f829dbdf 100644 --- a/src/muz/base/dl_boogie_proof.h +++ b/src/muz/base/dl_boogie_proof.h @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + /** output :: derivation model diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 30dd08ff4..c2db15e3a 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -28,6 +28,7 @@ Revision History: #include"datatype_decl_plugin.h" #include"scoped_proof.h" #include"fixedpoint_params.hpp" +#include"ast_pp_util.h" namespace datalog { @@ -210,14 +211,12 @@ namespace datalog { m_rewriter(m), m_var_subst(m), m_rule_manager(*this), - m_elim_unused_vars(m), - m_abstractor(m), m_contains_p(*this), - m_check_pred(m_contains_p, m), + m_rule_properties(m, m_rule_manager, *this, m_contains_p), m_transf(*this), m_trail(*this), m_pinned(m), - m_vars(m), + m_bind_variables(m), m_rule_set(*this), m_transformed_rule_set(*this), m_rule_fmls_head(0), @@ -228,11 +227,13 @@ namespace datalog { m_engine(0), m_closed(false), m_saturation_was_run(false), + m_enable_bind_variables(true), m_last_status(OK), m_last_answer(m), m_engine_type(LAST_ENGINE), m_cancel(false) { re.set_context(this); + updt_params(pa); } context::~context() { @@ -272,35 +273,40 @@ namespace datalog { } - bool context::generate_proof_trace() const { return m_params->generate_proof_trace(); } - bool context::output_profile() const { return m_params->output_profile(); } - bool context::output_tuples() const { return m_params->output_tuples(); } - bool context::use_map_names() const { return m_params->use_map_names(); } - bool context::fix_unbound_vars() const { return m_params->fix_unbound_vars(); } - symbol context::default_table() const { return m_params->default_table(); } - symbol context::default_relation() const { return m_params->default_relation(); } // external_relation_plugin::get_name()); - symbol context::default_table_checker() const { return m_params->default_table_checker(); } - bool context::default_table_checked() const { return m_params->default_table_checked(); } - bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->dbg_fpr_nonempty_relation_signature(); } - unsigned context::dl_profile_milliseconds_threshold() const { return m_params->profile_timeout_milliseconds(); } - bool context::all_or_nothing_deltas() const { return m_params->all_or_nothing_deltas(); } - bool context::compile_with_widening() const { return m_params->compile_with_widening(); } - bool context::unbound_compressor() const { return m_params->unbound_compressor(); } - bool context::similarity_compressor() const { return m_params->similarity_compressor(); } - unsigned context::similarity_compressor_threshold() const { return m_params->similarity_compressor_threshold(); } - unsigned context::timeout() const { return m_fparams.m_timeout; } - unsigned context::initial_restart_timeout() const { return m_params->initial_restart_timeout(); } - bool context::generate_explanations() const { return m_params->generate_explanations(); } - bool context::explanations_on_relation_level() const { return m_params->explanations_on_relation_level(); } - bool context::magic_sets_for_queries() const { return m_params->magic_sets_for_queries(); } - bool context::eager_emptiness_checking() const { return m_params->eager_emptiness_checking(); } - - bool context::bit_blast() const { return m_params->bit_blast(); } - bool context::karr() const { return m_params->karr(); } - bool context::scale() const { return m_params->scale(); } - bool context::magic() const { return m_params->magic(); } - bool context::quantify_arrays() const { return m_params->quantify_arrays(); } - bool context::instantiate_quantifiers() const { return m_params->instantiate_quantifiers(); } + bool context::generate_proof_trace() const { return m_generate_proof_trace; } + bool context::output_profile() const { return m_params->datalog_output_profile(); } + bool context::output_tuples() const { return m_params->datalog_print_tuples(); } + bool context::use_map_names() const { return m_params->datalog_use_map_names(); } + bool context::fix_unbound_vars() const { return m_params->xform_fix_unbound_vars(); } + symbol context::default_table() const { return m_params->datalog_default_table(); } + symbol context::default_relation() const { return m_default_relation; } + void context::set_default_relation(symbol const& s) { m_default_relation = s; } + symbol context::print_aig() const { return m_params->print_aig(); } + symbol context::check_relation() const { return m_params->datalog_check_relation(); } + symbol context::default_table_checker() const { return m_params->datalog_default_table_checker(); } + bool context::default_table_checked() const { return m_params->datalog_default_table_checked(); } + bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->datalog_dbg_fpr_nonempty_relation_signature(); } + unsigned context::dl_profile_milliseconds_threshold() const { return m_params->datalog_profile_timeout_milliseconds(); } + bool context::all_or_nothing_deltas() const { return m_params->datalog_all_or_nothing_deltas(); } + bool context::compile_with_widening() const { return m_params->datalog_compile_with_widening(); } + bool context::unbound_compressor() const { return m_unbound_compressor; } + void context::set_unbound_compressor(bool f) { m_unbound_compressor = f; } + bool context::similarity_compressor() const { return m_params->datalog_similarity_compressor(); } + unsigned context::similarity_compressor_threshold() const { return m_params->datalog_similarity_compressor_threshold(); } + unsigned context::soft_timeout() const { return m_fparams.m_timeout; } + unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); } + bool context::generate_explanations() const { return m_params->datalog_generate_explanations(); } + bool context::explanations_on_relation_level() const { return m_params->datalog_explanations_on_relation_level(); } + bool context::magic_sets_for_queries() const { return m_params->datalog_magic_sets_for_queries(); } + symbol context::tab_selection() const { return m_params->tab_selection(); } + bool context::xform_coi() const { return m_params->xform_coi(); } + bool context::xform_slice() const { return m_params->xform_slice(); } + bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); } + bool context::karr() const { return m_params->xform_karr(); } + bool context::scale() const { return m_params->xform_scale(); } + bool context::magic() const { return m_params->xform_magic(); } + bool context::quantify_arrays() const { return m_params->xform_quantify_arrays(); } + bool context::instantiate_quantifiers() const { return m_params->xform_instantiate_quantifiers(); } void context::register_finite_sort(sort * s, sort_kind k) { @@ -321,54 +327,23 @@ namespace datalog { } void context::register_variable(func_decl* var) { - m_vars.push_back(m.mk_const(var)); + m_bind_variables.add_var(m.mk_const(var)); } - expr_ref context::bind_variables(expr* fml, bool is_forall) { - expr_ref result(m); - app_ref_vector const & vars = m_vars; - rule_manager& rm = get_rule_manager(); - if (vars.empty()) { - result = fml; + expr_ref context::bind_vars(expr* fml, bool is_forall) { + if (m_enable_bind_variables) { + return m_bind_variables(fml, is_forall); } else { - m_names.reset(); - m_abstractor(0, vars.size(), reinterpret_cast(vars.c_ptr()), fml, result); - rm.collect_vars(result); - ptr_vector& sorts = rm.get_var_sorts(); - if (sorts.empty()) { - result = fml; - } - else { - for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - if (i < vars.size()) { - sorts[i] = vars[i]->get_decl()->get_range(); - } - else { - sorts[i] = m.mk_bool_sort(); - } - } - if (i < vars.size()) { - m_names.push_back(vars[i]->get_decl()->get_name()); - } - else { - m_names.push_back(symbol(i)); - } - } - quantifier_ref q(m); - sorts.reverse(); - q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), m_names.c_ptr(), result); - m_elim_unused_vars(q, result); - } + return expr_ref(fml, m); } - return result; } void context::register_predicate(func_decl * decl, bool named) { if (!is_predicate(decl)) { m_pinned.push_back(decl); m_preds.insert(decl); + TRACE("dl", tout << mk_pp(decl, m) << "\n";); if (named) { m_preds_by_name.insert(decl->get_name(), decl); } @@ -379,6 +354,7 @@ namespace datalog { m_preds.reset(); func_decl_set::iterator it = preds.begin(), end = preds.end(); for (; it != end; ++it) { + TRACE("dl", tout << mk_pp(*it, m) << "\n";); m_preds.insert(*it); } } @@ -490,12 +466,7 @@ namespace datalog { rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]); ++m_rule_fmls_head; } - rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); - rule_ref r(m_rule_manager); - for (; it != end; ++it) { - r = *it; - check_rule(r); - } + check_rules(m_rule_set); } // @@ -581,147 +552,58 @@ namespace datalog { m_engine->add_cover(level, pred, property); } - void context::check_uninterpreted_free(rule_ref& r) { - func_decl* f = 0; - if (r->has_uninterpreted_non_predicates(m, f)) { - std::stringstream stm; - stm << "Uninterpreted '" - << f->get_name() - << "' in "; - r->display(*this, stm); - throw default_exception(stm.str()); - } - } - - void context::check_quantifier_free(rule_ref& r) { - if (r->has_quantifiers()) { - std::stringstream stm; - stm << "cannot process quantifiers in rule "; - r->display(*this, stm); - throw default_exception(stm.str()); - } - } - - - void context::check_existential_tail(rule_ref& r) { - unsigned ut_size = r->get_uninterpreted_tail_size(); - unsigned t_size = r->get_tail_size(); - - TRACE("dl", r->display_smt2(get_manager(), tout); tout << "\n";); - for (unsigned i = ut_size; i < t_size; ++i) { - app* t = r->get_tail(i); - TRACE("dl", tout << "checking: " << mk_ismt2_pp(t, get_manager()) << "\n";); - if (m_check_pred(t)) { - std::ostringstream out; - out << "interpreted body " << mk_ismt2_pp(t, get_manager()) << " contains recursive predicate"; - throw default_exception(out.str()); - } - } - } - - void context::check_positive_predicates(rule_ref& r) { - ast_mark visited; - ptr_vector todo, tocheck; - unsigned ut_size = r->get_uninterpreted_tail_size(); - unsigned t_size = r->get_tail_size(); - for (unsigned i = 0; i < ut_size; ++i) { - if (r->is_neg_tail(i)) { - tocheck.push_back(r->get_tail(i)); - } - } - ast_manager& m = get_manager(); - contains_pred contains_p(*this); - check_pred check_pred(contains_p, get_manager()); - - for (unsigned i = ut_size; i < t_size; ++i) { - todo.push_back(r->get_tail(i)); - } - while (!todo.empty()) { - expr* e = todo.back(), *e1, *e2; - todo.pop_back(); - if (visited.is_marked(e)) { - continue; - } - visited.mark(e, true); - if (is_predicate(e)) { - } - else if (m.is_and(e) || m.is_or(e)) { - todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - } - else if (m.is_implies(e, e1, e2)) { - tocheck.push_back(e1); - todo.push_back(e2); - } - else if (is_quantifier(e)) { - todo.push_back(to_quantifier(e)->get_expr()); - } - else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && - m.is_true(e1)) { - todo.push_back(e2); - } - else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && - m.is_true(e2)) { - todo.push_back(e1); - } - else { - tocheck.push_back(e); - } - } - for (unsigned i = 0; i < tocheck.size(); ++i) { - expr* e = tocheck[i]; - if (check_pred(e)) { - std::ostringstream out; - out << "recursive predicate " << mk_ismt2_pp(e, get_manager()) << " occurs nested in body"; - r->display(*this, out << "\n"); - throw default_exception(out.str()); - - } - } - } - - void context::check_rule(rule_ref& r) { + void context::check_rules(rule_set& r) { + m_rule_properties.set_generate_proof(generate_proof_trace()); switch(get_engine()) { - case DATALOG_ENGINE: - check_quantifier_free(r); - check_uninterpreted_free(r); - check_existential_tail(r); + case DATALOG_ENGINE: + m_rule_properties.collect(r); + m_rule_properties.check_quantifier_free(); + m_rule_properties.check_uninterpreted_free(); + m_rule_properties.check_nested_free(); + m_rule_properties.check_infinite_sorts(); break; case PDR_ENGINE: - check_existential_tail(r); - check_positive_predicates(r); - check_uninterpreted_free(r); + m_rule_properties.collect(r); + m_rule_properties.check_existential_tail(); + m_rule_properties.check_for_negated_predicates(); + m_rule_properties.check_uninterpreted_free(); break; case QPDR_ENGINE: - check_positive_predicates(r); - check_uninterpreted_free(r); + m_rule_properties.collect(r); + m_rule_properties.check_for_negated_predicates(); + m_rule_properties.check_uninterpreted_free(); break; case BMC_ENGINE: - check_positive_predicates(r); + m_rule_properties.collect(r); + m_rule_properties.check_for_negated_predicates(); break; case QBMC_ENGINE: - check_existential_tail(r); - check_positive_predicates(r); + m_rule_properties.collect(r); + m_rule_properties.check_existential_tail(); + m_rule_properties.check_for_negated_predicates(); break; case TAB_ENGINE: - check_existential_tail(r); - check_positive_predicates(r); + m_rule_properties.collect(r); + m_rule_properties.check_existential_tail(); + m_rule_properties.check_for_negated_predicates(); break; case DUALITY_ENGINE: - check_existential_tail(r); - check_positive_predicates(r); + m_rule_properties.collect(r); + m_rule_properties.check_existential_tail(); + m_rule_properties.check_for_negated_predicates(); break; case CLP_ENGINE: - check_existential_tail(r); - check_positive_predicates(r); + m_rule_properties.collect(r); + m_rule_properties.check_existential_tail(); + m_rule_properties.check_for_negated_predicates(); + break; + case DDNF_ENGINE: break; case LAST_ENGINE: default: UNREACHABLE(); break; } - if (generate_proof_trace() && !r->get_proof()) { - m_rule_manager.mk_rule_asserted_proof(*r.get()); - } } void context::add_rule(rule_ref& r) { @@ -807,6 +689,7 @@ namespace datalog { } void context::transform_rules(rule_transformer::plugin* plugin) { + flet _enable_bv(m_enable_bind_variables, false); rule_transformer transformer(*this); transformer.register_plugin(plugin); transform_rules(transformer); @@ -847,6 +730,9 @@ namespace datalog { void context::updt_params(params_ref const& p) { m_params_ref.copy(p); if (m_engine.get()) m_engine->updt_params(); + m_generate_proof_trace = m_params->generate_proof_trace(); + m_unbound_compressor = m_params->datalog_unbound_compressor(); + m_default_relation = m_params->datalog_default_relation(); } expr_ref context::get_background_assertion() { @@ -908,6 +794,9 @@ namespace datalog { }; void context::configure_engine() { + if (m_engine_type != LAST_ENGINE) { + return; + } symbol e = m_params->engine(); if (e == symbol("datalog")) { @@ -934,6 +823,9 @@ namespace datalog { else if (e == symbol("duality")) { m_engine_type = DUALITY_ENGINE; } + else if (e == symbol("ddnf")) { + m_engine_type = DDNF_ENGINE; + } if (m_engine_type == LAST_ENGINE) { expr_fast_mark1 mark; @@ -959,18 +851,6 @@ namespace datalog { } lbool context::query(expr* query) { -#if 0 - // TODO: what? - if(get_engine() != DUALITY_ENGINE) { - new_query(); - rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); - rule_ref r(m_rule_manager); - for (; it != end; ++it) { - r = *it; - check_rule(r); - } - } -#endif m_mc = mk_skip_model_converter(); m_last_status = OK; m_last_answer = 0; @@ -982,6 +862,7 @@ namespace datalog { case QBMC_ENGINE: case TAB_ENGINE: case CLP_ENGINE: + case DDNF_ENGINE: flush_add_rules(); break; case DUALITY_ENGINE: @@ -1009,6 +890,7 @@ namespace datalog { void context::ensure_engine() { if (!m_engine.get()) { m_engine = m_register_engine.mk_engine(get_engine()); + m_engine->updt_params(); // break abstraction. if (get_engine() == DATALOG_ENGINE) { @@ -1065,6 +947,8 @@ namespace datalog { if (m_engine) { m_engine->collect_statistics(st); } + get_memory_statistics(st); + get_rlimit_statistics(m.limit(), st); } @@ -1075,31 +959,13 @@ namespace datalog { } // NB: algebraic data-types declarations will not be printed. - class free_func_visitor { - ast_manager& m; - func_decl_set m_funcs; - obj_hashtable m_sorts; - public: - free_func_visitor(ast_manager& m): m(m) {} - void operator()(var * n) { } - void operator()(app * n) { - m_funcs.insert(n->get_decl()); - sort* s = m.get_sort(n); - if (s->get_family_id() == null_family_id) { - m_sorts.insert(s); - } - } - void operator()(quantifier * n) { } - func_decl_set& funcs() { return m_funcs; } - obj_hashtable& sorts() { return m_sorts; } - }; static void collect_free_funcs(unsigned sz, expr* const* exprs, - expr_mark& visited, free_func_visitor& v, + ast_pp_util& v, mk_fresh_name& fresh_names) { + v.collect(sz, exprs); for (unsigned i = 0; i < sz; ++i) { expr* e = exprs[i]; - for_each_expr(v, visited, e); while (is_quantifier(e)) { e = to_quantifier(e)->get_expr(); } @@ -1107,25 +973,24 @@ namespace datalog { } } - void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names, vector &bounds){ + void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names, vector &bounds) { for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { - expr_ref r = bind_variables(m_rule_fmls[i].get(), true); + expr_ref r = bind_vars(m_rule_fmls[i].get(), true); rules.push_back(r.get()); - // rules.push_back(m_rule_fmls[i].get()); names.push_back(m_rule_names[i]); bounds.push_back(m_rule_bounds[i]); } } - void context::get_rules_as_formulas(expr_ref_vector& rules, svector& names) { + void context::get_rules_as_formulas(expr_ref_vector& rules, expr_ref_vector& queries, svector& names) { expr_ref fml(m); - datalog::rule_manager& rm = get_rule_manager(); + rule_manager& rm = get_rule_manager(); // ensure that rules are all using bound variables. for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { ptr_vector sorts; - get_free_vars(m_rule_fmls[i].get(), sorts); - if (!sorts.empty()) { + m_free_vars(m_rule_fmls[i].get()); + if (!m_free_vars.empty()) { rm.mk_rule(m_rule_fmls[i].get(), 0, m_rule_set, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); @@ -1137,9 +1002,30 @@ namespace datalog { } rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end(); for (; it != end; ++it) { - (*it)->to_formula(fml); - rules.push_back(fml); - names.push_back((*it)->name()); + rule* r = *it; + rm.to_formula(*r, fml); + func_decl* h = r->get_decl(); + if (m_rule_set.is_output_predicate(h)) { + expr* body = fml; + expr* e2; + if (is_quantifier(body)) { + quantifier* q = to_quantifier(body); + expr* e = q->get_expr(); + VERIFY(m.is_implies(e, body, e2)); + fml = m.mk_quantifier(false, q->get_num_decls(), + q->get_decl_sorts(), q->get_decl_names(), + body); + } + else { + VERIFY(m.is_implies(body, body, e2)); + fml = body; + } + queries.push_back(fml); + } + else { + rules.push_back(fml); + names.push_back(r->name()); + } } for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { rules.push_back(m_rule_fmls[i].get()); @@ -1147,37 +1033,33 @@ namespace datalog { } } - void context::display_smt2( - unsigned num_queries, - expr* const* queries, - std::ostream& out) { + void context::display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out) { ast_manager& m = get_manager(); - free_func_visitor visitor(m); - expr_mark visited; + ast_pp_util visitor(m); func_decl_set rels; unsigned num_axioms = m_background.size(); expr* const* axioms = m_background.c_ptr(); expr_ref fml(m); - expr_ref_vector rules(m); + expr_ref_vector rules(m), queries(m); svector names; - bool use_fixedpoint_extensions = m_params->print_with_fixedpoint_extensions(); + bool use_fixedpoint_extensions = m_params->print_fixedpoint_extensions(); bool print_low_level = m_params->print_low_level_smt2(); bool do_declare_vars = m_params->print_with_variable_declarations(); #define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env); - get_rules_as_formulas(rules, names); + get_rules_as_formulas(rules, queries, names); + queries.append(num_queries, qs); smt2_pp_environment_dbg env(m); mk_fresh_name fresh_names; - collect_free_funcs(num_axioms, axioms, visited, visitor, fresh_names); - collect_free_funcs(rules.size(), rules.c_ptr(), visited, visitor, fresh_names); - collect_free_funcs(num_queries, queries, visited, visitor, fresh_names); + collect_free_funcs(num_axioms, axioms, visitor, fresh_names); + collect_free_funcs(rules.size(), rules.c_ptr(), visitor, fresh_names); + collect_free_funcs(queries.size(), queries.c_ptr(), visitor, fresh_names); func_decl_set funcs; - func_decl_set::iterator it = visitor.funcs().begin(); - func_decl_set::iterator end = visitor.funcs().end(); - for (; it != end; ++it) { - func_decl* f = *it; + unsigned sz = visitor.coll.get_num_decls(); + for (unsigned i = 0; i < sz; ++i) { + func_decl* f = visitor.coll.get_func_decls()[i]; if (f->get_family_id() != null_family_id) { // } @@ -1192,20 +1074,9 @@ namespace datalog { if (!use_fixedpoint_extensions) { out << "(set-logic HORN)\n"; } - - it = funcs.begin(), end = funcs.end(); - - obj_hashtable& sorts = visitor.sorts(); - obj_hashtable::iterator sit = sorts.begin(), send = sorts.end(); - for (; sit != send; ++sit) { - PP(*sit); - } - for (; it != end; ++it) { - func_decl* f = *it; - PP(f); - out << "\n"; - } - it = rels.begin(); end = rels.end(); + + visitor.display_decls(out); + func_decl_set::iterator it = rels.begin(), end = rels.end(); for (; it != end; ++it) { func_decl* f = *it; out << "(declare-rel " << f->get_name() << " ("; @@ -1258,22 +1129,22 @@ namespace datalog { out << ")\n"; } if (use_fixedpoint_extensions) { - for (unsigned i = 0; i < num_queries; ++i) { + for (unsigned i = 0; i < queries.size(); ++i) { out << "(query "; - PP(queries[i]); + PP(queries[i].get()); out << ")\n"; } } else { - for (unsigned i = 0; i < num_queries; ++i) { - if (num_queries > 1) out << "(push)\n"; + for (unsigned i = 0; i < queries.size(); ++i) { + if (queries.size() > 1) out << "(push)\n"; out << "(assert "; expr_ref q(m); - q = m.mk_not(queries[i]); + q = m.mk_not(queries[i].get()); PP(q); out << ")\n"; out << "(check-sat)\n"; - if (num_queries > 1) out << "(pop)\n"; + if (queries.size() > 1) out << "(pop)\n"; } } } diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 8a881d71b..705758aca 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_CONTEXT_H_ -#define _DL_CONTEXT_H_ +#ifndef DL_CONTEXT_H_ +#define DL_CONTEXT_H_ #ifdef _CYGWIN #undef min @@ -39,9 +39,10 @@ Revision History: #include"model2expr.h" #include"smt_params.h" #include"dl_rule_transformer.h" -#include"expr_abstract.h" #include"expr_functors.h" #include"dl_engine_base.h" +#include"bind_variables.h" +#include"rule_properties.h" struct fixedpoint_params; @@ -142,19 +143,6 @@ namespace datalog { SK_UINT64, SK_SYMBOL }; - - private: - class sort_domain; - class symbol_sort_domain; - class uint64_sort_domain; - class restore_rules; - class contains_pred; - - typedef hashtable symbol_set; - typedef map sym2decl; - typedef obj_map > pred2syms; - typedef obj_map sort_domain_map; - class contains_pred : public i_expr_pred { context const& ctx; public: @@ -167,30 +155,43 @@ namespace datalog { }; + private: + class sort_domain; + class symbol_sort_domain; + class uint64_sort_domain; + class restore_rules; + + typedef hashtable symbol_set; + typedef map sym2decl; + typedef obj_map > pred2syms; + typedef obj_map sort_domain_map; + + ast_manager & m; register_engine_base& m_register_engine; smt_params & m_fparams; params_ref m_params_ref; fixedpoint_params* m_params; + bool m_generate_proof_trace; // cached configuration parameter + bool m_unbound_compressor; // cached configuration parameter + symbol m_default_relation; // cached configuration parameter dl_decl_util m_decl_util; th_rewriter m_rewriter; var_subst m_var_subst; rule_manager m_rule_manager; - unused_vars_eliminator m_elim_unused_vars; - expr_abstractor m_abstractor; contains_pred m_contains_p; - check_pred m_check_pred; + rule_properties m_rule_properties; rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; - app_ref_vector m_vars; - svector m_names; + bind_variables m_bind_variables; sort_domain_map m_sorts; func_decl_set m_preds; sym2decl m_preds_by_name; pred2syms m_argument_var_names; rule_set m_rule_set; rule_set m_transformed_rule_set; + expr_free_vars m_free_vars; unsigned m_rule_fmls_head; expr_ref_vector m_rule_fmls; svector m_rule_names; @@ -204,6 +205,7 @@ namespace datalog { bool m_closed; bool m_saturation_was_run; + bool m_enable_bind_variables; execution_result m_last_status; expr_ref m_last_answer; DL_ENGINE m_engine_type; @@ -249,27 +251,33 @@ namespace datalog { bool fix_unbound_vars() const; symbol default_table() const; symbol default_relation() const; - symbol default_table_checker() const; + void set_default_relation(symbol const& s); + symbol default_table_checker() const; + symbol check_relation() const; bool default_table_checked() const; bool dbg_fpr_nonempty_relation_signature() const; unsigned dl_profile_milliseconds_threshold() const; bool all_or_nothing_deltas() const; bool compile_with_widening() const; bool unbound_compressor() const; + void set_unbound_compressor(bool f); bool similarity_compressor() const; + symbol print_aig() const; + symbol tab_selection() const; unsigned similarity_compressor_threshold() const; - unsigned timeout() const; + unsigned soft_timeout() const; unsigned initial_restart_timeout() const; bool generate_explanations() const; bool explanations_on_relation_level() const; bool magic_sets_for_queries() const; - bool eager_emptiness_checking() const; - bool bit_blast() const; bool karr() const; bool scale() const; bool magic() const; bool quantify_arrays() const; bool instantiate_quantifiers() const; + bool xform_bit_blast() const; + bool xform_slice() const; + bool xform_coi() const; void register_finite_sort(sort * s, sort_kind k); @@ -286,12 +294,14 @@ namespace datalog { universal (if is_forall is true) or existential quantifier. */ - expr_ref bind_variables(expr* fml, bool is_forall); + expr_ref bind_vars(expr* fml, bool is_forall); + + bool& bind_vars_enabled() { return m_enable_bind_variables; } /** Register datalog relation. - If names is true, we associate the predicate with its name, so that it can be + If named is true, we associate the predicate with its name, so that it can be retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced e.g. by rule transformations do not need to be named. */ @@ -366,7 +376,7 @@ namespace datalog { rule_set & get_rules() { flush_add_rules(); return m_rule_set; } - void get_rules_as_formulas(expr_ref_vector& fmls, svector& names); + void get_rules_as_formulas(expr_ref_vector& fmls, expr_ref_vector& qs, svector& names); void get_raw_rule_formulas(expr_ref_vector& fmls, svector& names, vector &bounds); void add_fact(app * head); @@ -420,7 +430,7 @@ namespace datalog { /** \brief Check if rule is well-formed according to engine. */ - void check_rule(rule_ref& r); + void check_rules(rule_set& r); /** \brief Return true if facts to \c pred can be added using the \c add_table_fact() function. @@ -467,7 +477,7 @@ namespace datalog { void display(std::ostream & out) const; - void display_smt2(unsigned num_queries, expr* const* queries, std::ostream& out); + void display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out); void display_profile(std::ostream& out) const; @@ -566,11 +576,6 @@ namespace datalog { void ensure_engine(); - void check_quantifier_free(rule_ref& r); - void check_uninterpreted_free(rule_ref& r); - void check_existential_tail(rule_ref& r); - void check_positive_predicates(rule_ref& r); - // auxilary functions for SMT2 pretty-printer. void declare_vars(expr_ref_vector& rules, mk_fresh_name& mk_fresh, std::ostream& out); @@ -581,5 +586,5 @@ namespace datalog { }; -#endif /* _DL_CONTEXT_H_ */ +#endif /* DL_CONTEXT_H_ */ diff --git a/src/muz/base/dl_costs.h b/src/muz/base/dl_costs.h index 16cfc16b1..f3f97f6fd 100644 --- a/src/muz/base/dl_costs.h +++ b/src/muz/base/dl_costs.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _DL_COSTS_H_ -#define _DL_COSTS_H_ +#ifndef DL_COSTS_H_ +#define DL_COSTS_H_ #include @@ -111,5 +111,5 @@ namespace datalog { }; }; -#endif /* _DL_COSTS_H_ */ +#endif /* DL_COSTS_H_ */ diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index eaeebe979..e0d8680b2 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_ENGINE_BASE_H_ -#define _DL_ENGINE_BASE_H_ +#ifndef DL_ENGINE_BASE_H_ +#define DL_ENGINE_BASE_H_ #include "model.h" @@ -30,8 +30,9 @@ namespace datalog { QBMC_ENGINE, TAB_ENGINE, CLP_ENGINE, - LAST_ENGINE, - DUALITY_ENGINE + DUALITY_ENGINE, + DDNF_ENGINE, + LAST_ENGINE }; class engine_base { diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 1849903c5..aa010ab5a 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -56,7 +56,8 @@ namespace datalog { m_hnf(m), m_qe(m), m_cfg(m), - m_rwr(m, false, m_cfg) {} + m_rwr(m, false, m_cfg), + m_ufproc(m) {} void rule_manager::inc_ref(rule * r) { if (r) { @@ -111,16 +112,14 @@ namespace datalog { } void rule_manager::reset_collect_vars() { - m_vars.reset(); m_var_idx.reset(); - m_todo.reset(); - m_mark.reset(); + m_free_vars.reset(); } var_idx_set& rule_manager::finalize_collect_vars() { - unsigned sz = m_vars.size(); - for (unsigned i=0; i sorts; - ::get_free_vars(fml, sorts); ); expr_ref_vector fmls(m); proof_ref_vector prs(m); m_hnf.reset(); @@ -200,8 +197,6 @@ namespace datalog { m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false); } for (unsigned i = 0; i < fmls.size(); ++i) { - DEBUG_CODE(ptr_vector sorts; - ::get_free_vars(fmls[i].get(), sorts); ); mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name); } } @@ -228,7 +223,7 @@ namespace datalog { expr_ref fml1(m); if (p) { - r->to_formula(fml1); + to_formula(*r, fml1); if (fml1 == fml) { // no-op. } @@ -246,7 +241,7 @@ namespace datalog { if (p) { expr_ref fml2(m); - r->to_formula(fml2); + to_formula(*r, fml2); if (fml1 != fml2) { p = m.mk_modus_ponens(p, m.mk_rewrite(fml1, fml2)); } @@ -257,17 +252,16 @@ namespace datalog { unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) { expr* e1, *e2; - unsigned index = m_counter.get_next_var(fml); if (::is_forall(fml)) { - index += to_quantifier(fml)->get_num_decls(); fml = to_quantifier(fml)->get_expr(); } + unsigned index = m_counter.get_next_var(fml); if (m.is_implies(fml, e1, e2)) { - expr_ref_vector es(m); + m_args.reset(); head = ensure_app(e2); - qe::flatten_and(e1, es); - for (unsigned i = 0; i < es.size(); ++i) { - body.push_back(ensure_app(es[i].get())); + flatten_and(e1, m_args); + for (unsigned i = 0; i < m_args.size(); ++i) { + body.push_back(ensure_app(m_args[i].get())); } } else { @@ -299,7 +293,8 @@ namespace datalog { quantifier_hoister qh(m); qh.pull_quantifier(false, q, 0, &names); // retrieve free variables. - get_free_vars(q, vars); + m_free_vars(q); + vars.append(m_free_vars.size(), m_free_vars.c_ptr()); if (vars.contains(static_cast(0))) { var_subst sub(m, false); expr_ref_vector args(m); @@ -316,7 +311,8 @@ namespace datalog { } sub(q, args.size(), args.c_ptr(), q); vars.reset(); - get_free_vars(q, vars); + m_free_vars(q); + vars.append(m_free_vars.size(), m_free_vars.c_ptr()); } SASSERT(!vars.contains(static_cast(0)) && "Unused variables have been eliminated"); @@ -373,7 +369,7 @@ namespace datalog { } void rule_manager::bind_variables(expr* fml, bool is_forall, expr_ref& result) { - result = m_ctx.bind_variables(fml, is_forall); + result = m_ctx.bind_vars(fml, is_forall); } void rule_manager::flatten_body(app_ref_vector& body) { @@ -382,7 +378,7 @@ namespace datalog { for (unsigned i = 0; i < body.size(); ++i) { r.push_back(body[i].get()); } - qe::flatten_and(r); + flatten_and(r); body.reset(); for (unsigned i = 0; i < r.size(); ++i) { body.push_back(ensure_app(r[i].get())); @@ -498,11 +494,6 @@ namespace datalog { app * * uninterp_tail = r->m_tail; //grows upwards app * * interp_tail = r->m_tail+n; //grows downwards - DEBUG_CODE(ptr_vector sorts; - ::get_free_vars(head, sorts); - for (unsigned i = 0; i < n; ++i) { - ::get_free_vars(tail[i], sorts); - }); bool has_neg = false; @@ -556,11 +547,6 @@ namespace datalog { if (normalize) { r->norm_vars(*this); } - DEBUG_CODE(ptr_vector sorts; - ::get_free_vars(head, sorts); - for (unsigned i = 0; i < n; ++i) { - ::get_free_vars(tail[i], sorts); - }); return r; } @@ -587,6 +573,55 @@ namespace datalog { return r; } + void rule_manager::to_formula(rule const& r, expr_ref& fml) { + ast_manager & m = fml.get_manager(); + expr_ref_vector body(m); + for (unsigned i = 0; i < r.get_tail_size(); i++) { + body.push_back(r.get_tail(i)); + if (r.is_neg_tail(i)) { + body[body.size()-1] = m.mk_not(body.back()); + } + } + fml = r.get_head(); + switch (body.size()) { + case 0: break; + case 1: fml = m.mk_implies(body[0].get(), fml); break; + default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), fml); break; + } + + m_free_vars(fml); + if (m_free_vars.empty()) { + return; + } + svector names; + used_symbols<> us; + m_free_vars.set_default_sort(m.mk_bool_sort()); + + us(fml); + m_free_vars.reverse(); + for (unsigned j = 0, i = 0; i < m_free_vars.size(); ++j) { + for (char c = 'A'; i < m_free_vars.size() && c <= 'Z'; ++c) { + func_decl_ref f(m); + std::stringstream _name; + _name << c; + if (j > 0) _name << j; + symbol name(_name.str().c_str()); + if (!us.contains(name)) { + names.push_back(name); + ++i; + } + } + } + fml = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), fml); + } + + std::ostream& rule_manager::display_smt2(rule const& r, std::ostream & out) { + expr_ref fml(m); + to_formula(r, fml); + return out << mk_ismt2_pp(fml, m); + } + + void rule_manager::reduce_unbound_vars(rule_ref& r) { unsigned ut_len = r->get_uninterpreted_tail_size(); unsigned t_len = r->get_tail_size(); @@ -647,29 +682,25 @@ namespace datalog { svector tail_neg; app_ref head(r->get_head(), m); - collect_rule_vars(r); - vctr.count_vars(m, head); - ptr_vector& free_rule_vars = m_vars; + vctr.count_vars(head); for (unsigned i = 0; i < ut_len; i++) { app * t = r->get_tail(i); - vctr.count_vars(m, t); + vctr.count_vars(t); tail.push_back(t); tail_neg.push_back(r->is_neg_tail(i)); } - ptr_vector interp_vars; var_idx_set unbound_vars; expr_ref_vector tails_with_unbound(m); for (unsigned i = ut_len; i < t_len; i++) { app * t = r->get_tail(i); - interp_vars.reset(); - ::get_free_vars(t, interp_vars); + m_free_vars(t); bool has_unbound = false; - unsigned iv_size = interp_vars.size(); + unsigned iv_size = m_free_vars.size(); for (unsigned i=0; i qsorts; qsorts.resize(q_var_cnt); unsigned q_idx = 0; - for (unsigned v = 0; v <= max_var; ++v) { - sort * v_sort = free_rule_vars[v]; + for (unsigned v = 0; v < m_free_vars.size(); ++v) { + sort * v_sort = m_free_vars[v]; if (!v_sort) { //this variable index is not used continue; @@ -780,7 +810,7 @@ namespace datalog { !new_rule.get_proof() && old_rule.get_proof()) { expr_ref fml(m); - new_rule.to_formula(fml); + to_formula(new_rule, fml); scoped_proof _sc(m); proof* p = m.mk_rewrite(m.get_fact(old_rule.get_proof()), fml); new_rule.set_proof(m, m.mk_modus_ponens(old_rule.get_proof(), p)); @@ -791,7 +821,7 @@ namespace datalog { if (m_ctx.generate_proof_trace()) { scoped_proof _scp(m); expr_ref fml(m); - r.to_formula(fml); + to_formula(r, fml); r.set_proof(m, m.mk_asserted(fml)); } } @@ -881,84 +911,40 @@ namespace datalog { return false; } - struct uninterpreted_function_finder_proc { - ast_manager& m; - datatype_util m_dt; - dl_decl_util m_dl; - bool m_found; - func_decl* m_func; - uninterpreted_function_finder_proc(ast_manager& m): - m(m), m_dt(m), m_dl(m), m_found(false), m_func(0) {} - void operator()(var * n) { } - void operator()(quantifier * n) { } - void operator()(app * n) { - if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) { - m_found = true; - m_func = n->get_decl(); - } - else if (m_dt.is_accessor(n)) { - sort* s = m.get_sort(n->get_arg(0)); - SASSERT(m_dt.is_datatype(s)); - if (m_dt.get_datatype_constructors(s)->size() > 1) { - m_found = true; - m_func = n->get_decl(); - } - } - } - - bool found(func_decl*& f) const { f = m_func; return m_found; } - }; // // non-predicates may appear only in the interpreted tail, it is therefore // sufficient only to check the tail. // - bool rule::has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const { - unsigned sz = get_tail_size(); - uninterpreted_function_finder_proc proc(m); - expr_mark visited; - for (unsigned i = get_uninterpreted_tail_size(); i < sz && !proc.found(f); ++i) { - for_each_expr(proc, visited, get_tail(i)); + bool rule_manager::has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const { + unsigned sz = r.get_tail_size(); + m_ufproc.reset(); + m_visited.reset(); + for (unsigned i = r.get_uninterpreted_tail_size(); i < sz && !m_ufproc.found(f); ++i) { + for_each_expr_core(m_ufproc, m_visited, r.get_tail(i)); } - return proc.found(f); + return m_ufproc.found(f); } - struct quantifier_finder_proc { - bool m_exist; - bool m_univ; - quantifier_finder_proc() : m_exist(false), m_univ(false) {} - void operator()(var * n) { } - void operator()(quantifier * n) { - if (n->is_forall()) { - m_univ = true; - } - else { - SASSERT(n->is_exists()); - m_exist = true; - } - } - void operator()(app * n) { } - }; - // // Quantifiers may appear only in the interpreted tail, it is therefore // sufficient only to check the interpreted tail. // - void rule::has_quantifiers(bool& existential, bool& universal) const { - unsigned sz = get_tail_size(); - quantifier_finder_proc proc; - expr_mark visited; - for (unsigned i = get_uninterpreted_tail_size(); i < sz; ++i) { - for_each_expr(proc, visited, get_tail(i)); + void rule_manager::has_quantifiers(rule const& r, bool& existential, bool& universal) const { + unsigned sz = r.get_tail_size(); + m_qproc.reset(); + m_visited.reset(); + for (unsigned i = r.get_uninterpreted_tail_size(); i < sz; ++i) { + for_each_expr_core(m_qproc, m_visited, r.get_tail(i)); } - existential = proc.m_exist; - universal = proc.m_univ; + existential = m_qproc.m_exist; + universal = m_qproc.m_univ; } - bool rule::has_quantifiers() const { + bool rule_manager::has_quantifiers(rule const& r) const { bool exist, univ; - has_quantifiers(exist, univ); + has_quantifiers(r, exist, univ); return exist || univ; } @@ -1067,57 +1053,6 @@ namespace datalog { } } - void rule::to_formula(expr_ref& fml) const { - ast_manager & m = fml.get_manager(); - expr_ref_vector body(m); - for (unsigned i = 0; i < m_tail_size; i++) { - body.push_back(get_tail(i)); - if (is_neg_tail(i)) { - body[body.size()-1] = m.mk_not(body.back()); - } - } - switch(body.size()) { - case 0: fml = m_head; break; - case 1: fml = m.mk_implies(body[0].get(), m_head); break; - default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), m_head); break; - } - - ptr_vector sorts; - get_free_vars(fml, sorts); - if (sorts.empty()) { - return; - } - svector names; - used_symbols<> us; - for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } - } - - us(fml); - sorts.reverse(); - for (unsigned j = 0, i = 0; i < sorts.size(); ++j) { - for (char c = 'A'; i < sorts.size() && c <= 'Z'; ++c) { - func_decl_ref f(m); - std::stringstream _name; - _name << c; - if (j > 0) _name << j; - symbol name(_name.str().c_str()); - if (!us.contains(name)) { - names.push_back(name); - ++i; - } - } - } - fml = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), fml); - } - - std::ostream& rule::display_smt2(ast_manager& m, std::ostream & out) const { - expr_ref fml(m); - to_formula(fml); - return out << mk_ismt2_pp(fml, m); - } bool rule_eq_proc::operator()(const rule * r1, const rule * r2) const { if (r1->get_head()!=r2->get_head()) { return false; } diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index 7104bae1f..34cbffcb7 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _DL_RULE_H_ -#define _DL_RULE_H_ +#ifndef DL_RULE_H_ +#define DL_RULE_H_ #include"ast.h" #include"dl_costs.h" @@ -30,6 +30,8 @@ Revision History: #include"rewriter.h" #include"hnf.h" #include"qe_lite.h" +#include"var_subst.h" +#include"datatype_decl_plugin.h" namespace datalog { @@ -42,6 +44,56 @@ namespace datalog { typedef obj_ref rule_ref; typedef ref_vector rule_ref_vector; typedef ptr_vector rule_vector; + + + struct uninterpreted_function_finder_proc { + ast_manager& m; + datatype_util m_dt; + dl_decl_util m_dl; + bool m_found; + func_decl* m_func; + uninterpreted_function_finder_proc(ast_manager& m): + m(m), m_dt(m), m_dl(m), m_found(false), m_func(0) {} + void operator()(var * n) { } + void operator()(quantifier * n) { } + void operator()(app * n) { + if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) { + m_found = true; + m_func = n->get_decl(); + } + else if (m_dt.is_accessor(n)) { + sort* s = m.get_sort(n->get_arg(0)); + SASSERT(m_dt.is_datatype(s)); + if (m_dt.get_datatype_constructors(s)->size() > 1) { + m_found = true; + m_func = n->get_decl(); + } + } + } + void reset() { m_found = false; m_func = 0; } + + bool found(func_decl*& f) const { f = m_func; return m_found; } + }; + + struct quantifier_finder_proc { + bool m_exist; + bool m_univ; + quantifier_finder_proc() : m_exist(false), m_univ(false) {} + void operator()(var * n) { } + void operator()(quantifier * n) { + if (n->is_forall()) { + m_univ = true; + } + else { + SASSERT(n->is_exists()); + m_exist = true; + } + } + void operator()(app * n) { } + void reset() { m_exist = m_univ = false; } + }; + + /** \brief Manager for the \c rule class @@ -64,10 +116,8 @@ namespace datalog { context& m_ctx; rule_counter m_counter; used_vars m_used; - ptr_vector m_vars; var_idx_set m_var_idx; - ptr_vector m_todo; - ast_mark m_mark; + expr_free_vars m_free_vars; app_ref_vector m_body; app_ref m_head; expr_ref_vector m_args; @@ -76,6 +126,9 @@ namespace datalog { qe_lite m_qe; remove_label_cfg m_cfg; rewriter_tpl m_rwr; + mutable uninterpreted_function_finder_proc m_ufproc; + mutable quantifier_finder_proc m_qproc; + mutable expr_sparse_mark m_visited; // only the context can create a rule_manager @@ -143,7 +196,7 @@ namespace datalog { void accumulate_vars(expr* pred); - ptr_vector& get_var_sorts() { return m_vars; } + // ptr_vector& get_var_sorts() { return m_vars; } var_idx_set& get_var_idx() { return m_var_idx; } @@ -213,11 +266,18 @@ namespace datalog { */ bool is_fact(app * head) const; - static bool is_forall(ast_manager& m, expr* e, quantifier*& q); rule_counter& get_counter() { return m_counter; } + void to_formula(rule const& r, expr_ref& result); + + std::ostream& display_smt2(rule const& r, std::ostream & out); + + bool has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const; + void has_quantifiers(rule const& r, bool& existential, bool& universal) const; + bool has_quantifiers(rule const& r) const; + }; class rule : public accounted_object { @@ -286,6 +346,13 @@ namespace datalog { bool is_neg_tail(unsigned i) const { SASSERT(i < m_tail_size); return GET_TAG(m_tail[i]) == 1; } + /** + A predicate P(Xj) can be annotated by adding an interpreted predicate of the form ((_ min P N) ...) + where N is the column number that should be used for the min aggregation function. + Such an interpreted predicate is an example for which this function returns true. + */ + bool is_min_tail(unsigned i) const { return dl_decl_plugin::is_aggregate(get_tail(i)->get_decl()); } + /** Check whether predicate p is in the interpreted tail. @@ -293,9 +360,6 @@ namespace datalog { */ bool is_in_tail(const func_decl * p, bool only_positive=false) const; - bool has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const; - void has_quantifiers(bool& existential, bool& universal) const; - bool has_quantifiers() const; bool has_negation() const; /** @@ -306,12 +370,8 @@ namespace datalog { void get_vars(ast_manager& m, ptr_vector& sorts) const; - void to_formula(expr_ref& result) const; - void display(context & ctx, std::ostream & out) const; - std::ostream& display_smt2(ast_manager& m, std::ostream & out) const; - symbol const& name() const { return m_name; } unsigned hash() const; @@ -327,5 +387,5 @@ namespace datalog { }; -#endif /* _DL_RULE_H_ */ +#endif /* DL_RULE_H_ */ diff --git a/src/muz/base/dl_rule_set.cpp b/src/muz/base/dl_rule_set.cpp index ad3b512a3..4f558a535 100644 --- a/src/muz/base/dl_rule_set.cpp +++ b/src/muz/base/dl_rule_set.cpp @@ -400,7 +400,7 @@ namespace datalog { SASSERT(!is_closed()); //the rule_set is not already closed m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); - if (!stratified_negation()) { + if (!stratified_negation() || !check_min()) { m_stratifier = 0; m_deps.reset(); return false; @@ -441,6 +441,49 @@ namespace datalog { return true; } + bool rule_set::check_min() { + // For now, we check the following: + // + // if a min aggregation function occurs in an SCC, is this SCC + // free of any other non-monotonic functions, e.g. negation? + const unsigned NEG_BIT = 1U << 0; + const unsigned MIN_BIT = 1U << 1; + + ptr_vector::const_iterator it = m_rules.c_ptr(); + ptr_vector::const_iterator end = m_rules.c_ptr() + m_rules.size(); + unsigned_vector component_status(m_stratifier->get_strats().size()); + + for (; it != end; it++) { + rule * r = *it; + // app * head = r->get_head(); + // func_decl * head_decl = head->get_decl(); + // unsigned head_strat = get_predicate_strat(head_decl); + unsigned n = r->get_tail_size(); + for (unsigned i = 0; i < n; i++) { + func_decl * tail_decl = r->get_tail(i)->get_decl(); + unsigned strat = get_predicate_strat(tail_decl); + + if (r->is_neg_tail(i)) { + SASSERT(strat < component_status.size()); + component_status[strat] |= NEG_BIT; + } + + if (r->is_min_tail(i)) { + SASSERT(strat < component_status.size()); + component_status[strat] |= MIN_BIT; + } + } + } + + const unsigned CONFLICT = NEG_BIT | MIN_BIT; + for (unsigned k = 0; k < component_status.size(); ++k) { + if (component_status[k] == CONFLICT) + return false; + } + + return true; + } + void rule_set::replace_rules(const rule_set & src) { if (this != &src) { reset(); diff --git a/src/muz/base/dl_rule_set.h b/src/muz/base/dl_rule_set.h index c1fc7ea3f..b6735aba9 100644 --- a/src/muz/base/dl_rule_set.h +++ b/src/muz/base/dl_rule_set.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_RULE_SET_H_ -#define _DL_RULE_SET_H_ +#ifndef DL_RULE_SET_H_ +#define DL_RULE_SET_H_ #include"obj_hashtable.h" #include"dl_rule.h" @@ -39,10 +39,10 @@ namespace datalog { Each master object is also present as a key of the map, even if its master set is empty. */ - deps_type m_data; - context & m_context; + deps_type m_data; + context & m_context; ptr_vector m_todo; - ast_mark m_visited; + expr_sparse_mark m_visited; //we need to take care with removing to avoid memory leaks @@ -179,6 +179,7 @@ namespace datalog { void compute_deps(); void compute_tc_deps(); bool stratified_negation(); + bool check_min(); public: rule_set(context & ctx); rule_set(const rule_set & rs); @@ -278,5 +279,5 @@ namespace datalog { }; -#endif /* _DL_RULE_SET_H_ */ +#endif /* DL_RULE_SET_H_ */ diff --git a/src/muz/base/dl_rule_subsumption_index.h b/src/muz/base/dl_rule_subsumption_index.h index 44befc9bb..89f7d44b7 100644 --- a/src/muz/base/dl_rule_subsumption_index.h +++ b/src/muz/base/dl_rule_subsumption_index.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef _DL_RULE_SUBSUMPTION_INDEX_H_ -#define _DL_RULE_SUBSUMPTION_INDEX_H_ +#ifndef DL_RULE_SUBSUMPTION_INDEX_H_ +#define DL_RULE_SUBSUMPTION_INDEX_H_ #include "dl_context.h" @@ -61,5 +61,5 @@ namespace datalog { }; -#endif /* _DL_RULE_SUBSUMPTION_INDEX_H_ */ +#endif /* DL_RULE_SUBSUMPTION_INDEX_H_ */ diff --git a/src/muz/base/dl_rule_transformer.h b/src/muz/base/dl_rule_transformer.h index 3f60e9aea..d166acdd0 100644 --- a/src/muz/base/dl_rule_transformer.h +++ b/src/muz/base/dl_rule_transformer.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_RULE_TRANSFORMER_H_ -#define _DL_RULE_TRANSFORMER_H_ +#ifndef DL_RULE_TRANSFORMER_H_ +#define DL_RULE_TRANSFORMER_H_ #include"map.h" #include"vector.h" @@ -113,5 +113,5 @@ namespace datalog { }; }; -#endif /* _DL_RULE_TRANSFORMER_H_ */ +#endif /* DL_RULE_TRANSFORMER_H_ */ diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index a6647a1d2..0f254ee0a 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -56,9 +56,9 @@ namespace datalog { bool contains_var(expr * trm, unsigned var_idx) { - ptr_vector vars; - ::get_free_vars(trm, vars); - return var_idx < vars.size() && vars[var_idx] != 0; + expr_free_vars fv; + fv(trm); + return fv.contains(var_idx); } unsigned count_variable_arguments(app * pred) @@ -256,12 +256,12 @@ namespace datalog { } - void rule_counter::count_rule_vars(ast_manager & m, const rule * r, int coef) { + void rule_counter::count_rule_vars(const rule * r, int coef) { reset(); - count_vars(m, r->get_head(), 1); + count_vars(r->get_head(), 1); unsigned n = r->get_tail_size(); for (unsigned i = 0; i < n; i++) { - count_vars(m, r->get_tail(i), coef); + count_vars(r->get_tail(i), coef); } } @@ -300,14 +300,15 @@ namespace datalog { } - void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, + void resolve_rule(rule_manager& rm, + replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res) { if (!pc) return; ast_manager& m = s1.get_manager(); expr_ref fml1(m), fml2(m), fml3(m); - r1.to_formula(fml1); - r2.to_formula(fml2); - res.to_formula(fml3); + rm.to_formula(r1, fml1); + rm.to_formula(r2, fml2); + rm.to_formula(res, fml3); vector substs; svector > positions; substs.push_back(s1); @@ -337,7 +338,7 @@ namespace datalog { pc->insert(pr); } - void resolve_rule(rule const& r1, rule const& r2, unsigned idx, + void resolve_rule(rule_manager& rm, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res) { if (!r1.get_proof()) { return; @@ -345,7 +346,7 @@ namespace datalog { SASSERT(r2.get_proof()); ast_manager& m = s1.get_manager(); expr_ref fml(m); - res.to_formula(fml); + rm.to_formula(res, fml); vector substs; svector > positions; substs.push_back(s1); diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 57f7575ca..8b4344ce6 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_UTIL_H_ -#define _DL_UTIL_H_ +#ifndef DL_UTIL_H_ +#define DL_UTIL_H_ #include"ast.h" #include"hashtable.h" @@ -41,12 +41,13 @@ namespace datalog { class pentagon_relation; class relation_fact; class relation_signature; + class rule_manager; class verbose_action { unsigned m_lvl; class stopwatch* m_sw; public: - verbose_action(char const* msg, unsigned lvl = 1); + verbose_action(char const* msg, unsigned lvl = 11); ~verbose_action(); }; @@ -345,17 +346,19 @@ namespace datalog { class rule_counter : public var_counter { public: - rule_counter(bool stay_non_negative = true): var_counter(stay_non_negative) {} - void count_rule_vars(ast_manager & m, const rule * r, int coef = 1); + rule_counter(){} + void count_rule_vars(const rule * r, int coef = 1); unsigned get_max_rule_var(const rule& r); }; void del_rule(horn_subsume_model_converter* mc, rule& r); - void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, + void resolve_rule(rule_manager& rm, + replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res); - void resolve_rule(rule const& r1, rule const& r2, unsigned idx, + void resolve_rule(rule_manager& rm, + rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res); model_converter* mk_skip_model_converter(); @@ -730,5 +733,5 @@ namespace datalog { bool read_uint64(const char * & s, uint64 & res); }; -#endif /* _DL_UTIL_H_ */ +#endif /* DL_UTIL_H_ */ diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 832e3329e..86cfb30ac 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -2,83 +2,148 @@ def_module_params('fixedpoint', description='fixedpoint parameters', export=True, params=(('timeout', UINT, UINT_MAX, 'set timeout'), - ('engine', SYMBOL, 'auto-config', 'Select: auto-config, datalog, pdr, bmc'), - ('default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'), - ('default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'), - ('generate_explanations', BOOL, False, '(DATALOG) produce explanations for produced facts when using the datalog engine'), - ('use_map_names', BOOL, True, "(DATALOG) use names from map files when displaying tuples"), - ('bit_blast', BOOL, False, '(PDR) bit-blast bit-vectors'), - ('explanations_on_relation_level', BOOL, False, '(DATALOG) if true, explanations are generated as history of each relation, rather than per fact (generate_explanations must be set to true for this option to have any effect)'), - ('magic_sets_for_queries', BOOL, False, "(DATALOG) magic set transformation will be used for queries"), - ('magic', BOOL, False, "perform symbolic magic set transformation"), - ('scale', BOOL, False, "add scaling variable to linear real arithemtic clauses"), - ('unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables in rule heads"), - ('similarity_compressor', BOOL, True, "(DATALOG) rules that differ only in values of constants will be merged into a single rule"), - ('similarity_compressor_threshold', UINT, 11, "(DATALOG) if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged"), - ('all_or_nothing_deltas', BOOL, False, "(DATALOG) compile rules so that it is enough for the delta relation in union and widening operations to determine only whether the updated relation was modified or not"), - ('compile_with_widening', BOOL, False, "(DATALOG) widening will be used to compile recursive rules"), - ('eager_emptiness_checking', BOOL, True, "(DATALOG) emptiness of affected relations will be checked after each instruction, so that we may ommit unnecessary instructions"), - ('default_table_checked', BOOL, False, "if true, the detault table will be default_table inside a wrapper that checks that its results are the same as of default_table_checker table"), - ('default_table_checker', SYMBOL, 'null', "see default_table_checked"), - - - ('initial_restart_timeout', UINT, 0, "length of saturation run before the first restart (in ms), zero means no restarts"), - ('output_profile', BOOL, False, "determines whether profile informations should be output when outputting Datalog rules or instructions"), - ('inline_linear', BOOL, True, "try linear inlining method"), - ('inline_eager', BOOL, True, "try eager inlining of rules"), - ('inline_linear_branch', BOOL, False, "try linear inlining method with potential expansion"), - ('fix_unbound_vars', BOOL, False, "fix unbound variables in tail"), - ('output_tuples', BOOL, True, "determines whether tuples for output predicates should be output"), - ('print_with_fixedpoint_extensions', BOOL, True, "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, when printing rules"), - ('print_low_level_smt2', BOOL, False, "use (faster) low-level SMT2 printer (the printer is scalable but the result may not be as readable)"), - ('print_with_variable_declarations', BOOL, True, "use variable declarations when displaying rules (instead of attempting to use original names)"), - ('bfs_model_search', BOOL, True, "PDR: use BFS strategy for expanding model search"), - ('use_farkas', BOOL, True, "PDR: use lemma generator based on Farkas (for linear real arithmetic)"), - ('generate_proof_trace', BOOL, False, "PDR: trace for 'sat' answer as proof object"), - ('flexible_trace', BOOL, False, "PDR: allow PDR generate long counter-examples " - "by extending candidate trace within search area"), - ('unfold_rules', UINT, 0, "PDR: unfold rules statically using iterative squarring"), - ('use_model_generalizer', BOOL, False, "PDR: use model for backwards propagation (instead of symbolic simulation)"), - ('validate_result', BOOL, False, "PDR validate result (by proof checking or model checking)"), - ('simplify_formulas_pre', BOOL, False, "PDR: simplify derived formulas before inductive propagation"), - ('simplify_formulas_post', BOOL, False, "PDR: simplify derived formulas after inductive propagation"), - ('slice', BOOL, True, "PDR: simplify clause set using slicing"), - ('karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), - ('quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"), - ('instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"), - ('coalesce_rules', BOOL, False, "BMC: coalesce rules"), - ('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"), - ('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"), - ('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"), - ('use_convex_closure_generalizer', BOOL, False, "PDR: generalize using convex closures of lemmas"), - ('use_convex_interior_generalizer', BOOL, False, "PDR: generalize using convex interiors of lemmas"), - ('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"), - ('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when " - "checking for reachability (not only during cube weakening)"), - ('max_num_contexts', UINT, 500, "PDR: maximal number of contexts to create"), - ('try_minimize_core', BOOL, False, "PDR: try to reduce core size (before inductive minimization)"), - ('profile_timeout_milliseconds', UINT, 0, "instructions and rules that took less than the threshold will not be printed when printed the instruction/rule list"), - ('dbg_fpr_nonempty_relation_signature', BOOL, False, - "if true, finite_product_relation will attempt to avoid creating inner relation with empty signature " - "by putting in half of the table columns, if it would have been empty otherwise"), + ('engine', SYMBOL, 'auto-config', + 'Select: auto-config, datalog, duality, pdr, bmc'), + ('datalog.default_table', SYMBOL, 'sparse', + 'default table implementation: sparse, hashtable, bitvector, interval'), + ('datalog.default_relation', SYMBOL, 'pentagon', + 'default relation implementation: external_relation, pentagon'), + ('datalog.generate_explanations', BOOL, False, + 'produce explanations for produced facts when using the datalog engine'), + ('datalog.use_map_names', BOOL, True, + "use names from map files when displaying tuples"), + ('datalog.magic_sets_for_queries', BOOL, False, + "magic set transformation will be used for queries"), + ('datalog.explanations_on_relation_level', BOOL, False, + 'if true, explanations are generated as history of each relation, ' + + 'rather than per fact (generate_explanations must be set to true for ' + + 'this option to have any effect)'), + ('datalog.unbound_compressor', BOOL, True, + "auxiliary relations will be introduced to avoid unbound variables " + + "in rule heads"), + ('datalog.similarity_compressor', BOOL, True, + "rules that differ only in values of constants will be merged into " + + "a single rule"), + ('datalog.similarity_compressor_threshold', UINT, 11, + "if similarity_compressor is on, this value determines how many " + + "similar rules there must be in order for them to be merged"), + ('datalog.all_or_nothing_deltas', BOOL, False, + "compile rules so that it is enough for the delta relation in " + + "union and widening operations to determine only whether the " + + "updated relation was modified or not"), + ('datalog.compile_with_widening', BOOL, False, + "widening will be used to compile recursive rules"), + ('datalog.default_table_checked', BOOL, False, "if true, the detault " + + 'table will be default_table inside a wrapper that checks that its results ' + + 'are the same as of default_table_checker table'), + ('datalog.default_table_checker', SYMBOL, 'null', "see default_table_checked"), + ('datalog.check_relation',SYMBOL,'null', "name of default relation to check. " + + "operations on the default relation will be verified using SMT solving"), + ('datalog.initial_restart_timeout', UINT, 0, + "length of saturation run before the first restart (in ms), " + + "zero means no restarts"), + ('datalog.output_profile', BOOL, False, + "determines whether profile information should be " + + "output when outputting Datalog rules or instructions"), + ('datalog.print.tuples', BOOL, True, + "determines whether tuples for output predicates should be output"), + ('datalog.profile_timeout_milliseconds', UINT, 0, + "instructions and rules that took less than the threshold " + + "will not be printed when printed the instruction/rule list"), + ('datalog.dbg_fpr_nonempty_relation_signature', BOOL, False, + "if true, finite_product_relation will attempt to avoid creating " + + "inner relation with empty signature by putting in half of the " + + "table columns, if it would have been empty otherwise"), + ('duality.full_expand', BOOL, False, 'Fully expand derivation trees'), + ('duality.no_conj', BOOL, False, 'No forced covering (conjectures)'), + ('duality.feasible_edges', BOOL, True, + 'Don\'t expand definitley infeasible edges'), + ('duality.use_underapprox', BOOL, False, 'Use underapproximations'), + ('duality.stratified_inlining', BOOL, False, 'Use stratified inlining'), + ('duality.recursion_bound', UINT, UINT_MAX, + 'Recursion bound for stratified inlining'), + ('duality.profile', BOOL, False, 'profile run time'), + ('duality.mbqi', BOOL, True, 'use model-based quantifier instantiation'), + ('duality.batch_expand', BOOL, False, 'use batch expansion'), + ('duality.conjecture_file', STRING, '', 'save conjectures to file'), + ('pdr.bfs_model_search', BOOL, True, + "use BFS strategy for expanding model search"), + ('pdr.farkas', BOOL, True, + "use lemma generator based on Farkas (for linear real arithmetic)"), + ('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"), + ('pdr.flexible_trace', BOOL, False, + "allow PDR generate long counter-examples " + + "by extending candidate trace within search area"), + ('pdr.use_model_generalizer', BOOL, False, + "use model for backwards propagation (instead of symbolic simulation)"), + ('pdr.validate_result', BOOL, False, + "validate result (by proof checking or model checking)"), + ('pdr.simplify_formulas_pre', BOOL, False, + "simplify derived formulas before inductive propagation"), + ('pdr.simplify_formulas_post', BOOL, False, + "simplify derived formulas after inductive propagation"), + ('pdr.use_multicore_generalizer', BOOL, False, + "extract multiple cores for blocking states"), + ('pdr.use_inductive_generalizer', BOOL, True, + "generalize lemmas using induction strengthening"), + ('pdr.use_arith_inductive_generalizer', BOOL, False, + "generalize lemmas using arithmetic heuristics for induction strengthening"), + ('pdr.use_convex_closure_generalizer', BOOL, False, + "generalize using convex closures of lemmas"), + ('pdr.use_convex_interior_generalizer', BOOL, False, + "generalize using convex interiors of lemmas"), + ('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " + + "cache (2) for model search"), + ('pdr.inductive_reachability_check', BOOL, False, + "assume negation of the cube on the previous level when " + + "checking for reachability (not only during cube weakening)"), + ('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"), + ('pdr.try_minimize_core', BOOL, False, + "try to reduce core size (before inductive minimization)"), + ('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'), + ('print_fixedpoint_extensions', BOOL, True, + "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + + "when printing rules"), + ('print_low_level_smt2', BOOL, False, + "use (faster) low-level SMT2 printer (the printer is scalable " + + "but the result may not be as readable)"), + ('print_with_variable_declarations', BOOL, True, + "use variable declarations when displaying rules " + + "(instead of attempting to use original names)"), ('print_answer', BOOL, False, 'print answer instance(s) to query'), - ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), - ('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a format understood by Boogie'), + ('print_certificate', BOOL, False, + 'print certificate for reachability or non-reachability'), + ('print_boogie_certificate', BOOL, False, + 'print certificate for reachability or non-reachability using a ' + + 'format understood by Boogie'), ('print_statistics', BOOL, False, 'print statistics'), - ('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'), - ('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), - ('full_expand', BOOL, False, 'DUALITY: Fully expand derivation trees'), - ('no_conj', BOOL, False, 'DUALITY: No forced covering (conjectures)'), - ('feasible_edges', BOOL, True, 'DUALITY: Don\'t expand definitley infeasible edges'), - ('use_underapprox', BOOL, False, 'DUALITY: Use underapproximations'), - ('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'), - ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), - ('profile', BOOL, False, 'DUALITY: profile run time'), - ('mbqi', BOOL, True, 'DUALITY: use model-based quantifier instantion'), - ('batch_expand', BOOL, False, 'DUALITY: use batch expansion'), - ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), - ('conjecture_file', STRING, '', 'DUALITY: save conjectures to file'), - ('enable_restarts', BOOL, False, 'DUALITY: enable restarts'), + ('print_aig', SYMBOL, '', + 'Dump clauses in AIG text format (AAG) to the given file name'), + ('tab.selection', SYMBOL, 'weight', + 'selection method for tabular strategy: weight (default), first, var-use'), + ('xform.bit_blast', BOOL, False, + 'bit-blast bit-vectors'), + ('xform.magic', BOOL, False, + "perform symbolic magic set transformation"), + ('xform.scale', BOOL, False, + "add scaling variable to linear real arithemtic clauses"), + ('xform.inline_linear', BOOL, True, "try linear inlining method"), + ('xform.inline_eager', BOOL, True, "try eager inlining of rules"), + ('xform.inline_linear_branch', BOOL, False, + "try linear inlining method with potential expansion"), + ('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"), + ('xform.unfold_rules', UINT, 0, + "unfold rules statically using iterative squarring"), + ('xform.slice', BOOL, True, "simplify clause set using slicing"), + ('xform.karr', BOOL, False, + "Add linear invariants to clauses using Karr's method"), + ('xform.quantify_arrays', BOOL, False, + "create quantified Horn clauses from clauses with arrays"), + ('xform.instantiate_quantifiers', BOOL, False, + "instantiate quantified Horn clauses using E-matching heuristic"), + ('xform.coalesce_rules', BOOL, False, "coalesce rules"), + ('xform.coi', BOOL, True, "use cone of influence simplificaiton"), + ('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'), )) diff --git a/src/muz/base/hnf.cpp b/src/muz/base/hnf.cpp index 9d6f3c1ab..54bd727d6 100644 --- a/src/muz/base/hnf.cpp +++ b/src/muz/base/hnf.cpp @@ -53,11 +53,25 @@ Notes: #include"cooperate.h" #include"ast_pp.h" #include"quant_hoist.h" +#include"ast_util.h" #include"dl_util.h" #include"for_each_ast.h" #include"for_each_expr.h" class hnf::imp { + + class contains_predicate_proc { + imp const& m; + public: + struct found {}; + contains_predicate_proc(imp const& m): m(m) {} + void operator()(var * n) {} + void operator()(quantifier * n) {} + void operator()(app* n) { + if (m.is_predicate(n)) throw found(); + } + }; + ast_manager& m; bool m_produce_proofs; volatile bool m_cancel; @@ -73,6 +87,9 @@ class hnf::imp { func_decl_ref_vector m_fresh_predicates; expr_ref_vector m_body; proof_ref_vector m_defs; + contains_predicate_proc m_proc; + expr_free_vars m_free_vars; + ast_fast_mark1 m_mark1; public: @@ -87,13 +104,47 @@ public: m_qh(m), m_fresh_predicates(m), m_body(m), - m_defs(m) { + m_defs(m), + m_proc(*this) { + } + + bool is_horn(expr* n) { + expr* n1, *n2; + while (is_forall(n)) n = to_quantifier(n)->get_expr(); + if (m.is_implies(n, n1, n2) && is_predicate(n2)) { + if (is_var(n1)) { + return true; + } + if (is_quantifier(n1)) { + return false; + } + app* a1 = to_app(n1); + if (m.is_and(a1)) { + for (unsigned i = 0; i < a1->get_num_args(); ++i) { + if (!is_predicate(a1->get_arg(i)) && + contains_predicate(a1->get_arg(i))) { + return false; + } + } + } + else if (!is_predicate(a1) && contains_predicate(a1)) { + return false; + } + return true; + } + + return false; } void operator()(expr * n, proof* p, expr_ref_vector& result, proof_ref_vector& ps) { + if (is_horn(n)) { + result.push_back(n); + ps.push_back(p); + return; + } expr_ref fml(m); proof_ref pr(m); m_todo.reset(); @@ -166,24 +217,13 @@ private: return m.is_bool(f->get_range()) && f->get_family_id() == null_family_id; } - class contains_predicate_proc { - imp const& m; - public: - struct found {}; - contains_predicate_proc(imp const& m): m(m) {} - void operator()(var * n) {} - void operator()(quantifier * n) {} - void operator()(app* n) { - if (m.is_predicate(n)) throw found(); - } - }; - - bool contains_predicate(expr* fml) const { - contains_predicate_proc proc(*this); + bool contains_predicate(expr* fml) { try { - quick_for_each_expr(proc, fml); + quick_for_each_expr(m_proc, m_mark1, fml); + m_mark1.reset(); } catch (contains_predicate_proc::found) { + m_mark1.reset(); return true; } return false; @@ -214,7 +254,7 @@ private: m_body.push_back(e1); head = e2; } - qe::flatten_and(m_body); + flatten_and(m_body); if (premise) { p = m.mk_rewrite(fml0, mk_implies(m_body, head)); } @@ -348,13 +388,13 @@ private: } app_ref mk_fresh_head(expr* e) { - ptr_vector sorts0, sorts1; - get_free_vars(e, sorts0); + ptr_vector sorts1; + m_free_vars(e); expr_ref_vector args(m); - for (unsigned i = 0; i < sorts0.size(); ++i) { - if (sorts0[i]) { - args.push_back(m.mk_var(i, sorts0[i])); - sorts1.push_back(sorts0[i]); + for (unsigned i = 0; i < m_free_vars.size(); ++i) { + if (m_free_vars[i]) { + args.push_back(m.mk_var(i, m_free_vars[i])); + sorts1.push_back(m_free_vars[i]); } } func_decl_ref f(m); diff --git a/src/muz/base/hnf.h b/src/muz/base/hnf.h index 37339540b..35fc5fafc 100644 --- a/src/muz/base/hnf.h +++ b/src/muz/base/hnf.h @@ -1,37 +1,42 @@ + /*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - hnf.h - -Abstract: - - Horn normal form convertion. -Author: - - -Notes: - - Very similar to NNF. +Copyright (c) 2015 Microsoft Corporation --*/ -#ifndef _HNF_H_ -#define _HNF_H_ +/*-- + Module Name: + + hnf.h + + Abstract: + + Horn normal form convertion. + Author: + + + Notes: + + Very similar to NNF. + +--*/ + +#ifndef HNF_H_ +#define HNF_H_ + #include"ast.h" #include"params.h" #include"defined_names.h" #include"proof_converter.h" - + class hnf { class imp; imp * m_imp; -public: + public: hnf(ast_manager & m); ~hnf(); - + void operator()(expr * n, // [IN] expression that should be put into Horn NF proof* p, // [IN] proof of n expr_ref_vector & rs, // [OUT] resultant (conjunction) of expressions @@ -45,5 +50,5 @@ public: void reset(); func_decl_ref_vector const& get_fresh_predicates(); }; - -#endif /* _HNF_H_ */ + +#endif /* HNF_H_ */ diff --git a/src/muz/base/proof_utils.cpp b/src/muz/base/proof_utils.cpp index 87ecf9985..59856c160 100644 --- a/src/muz/base/proof_utils.cpp +++ b/src/muz/base/proof_utils.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "dl_util.h" #include "proof_utils.h" #include "ast_smt2_pp.h" @@ -156,12 +162,22 @@ public: SASSERT(m.get_num_parents(p) == 1); tmp = m.get_parent(p, 0); elim(tmp); - get_literals(m.get_fact(p)); expr_set* hyps = m_hypmap.find(tmp); expr_set* new_hyps = 0; if (hyps) { new_hyps = alloc(expr_set, *hyps); } + expr* fact = m.get_fact(p); + // when hypothesis is a single literal of the form + // (or A B), and the fact of p is (or A B). + if (hyps && hyps->size() == 1 && in_hypotheses(fact, hyps)) { + m_literals.reset(); + m_literals.push_back(fact); + } + else { + get_literals(fact); + } + for (unsigned i = 0; i < m_literals.size(); ++i) { expr* e = m_literals[i]; if (!in_hypotheses(e, hyps)) { @@ -509,7 +525,7 @@ static void permute_unit_resolution(expr_ref_vector& refs, obj_map cache.insert(pr, prNew); refs.push_back(prNew); pr = prNew; -} +} // permute unit resolution over Theory lemmas to track premises. diff --git a/src/muz/base/proof_utils.h b/src/muz/base/proof_utils.h index 383b5c379..0db831c4f 100644 --- a/src/muz/base/proof_utils.h +++ b/src/muz/base/proof_utils.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _PROOF_UTILS_H_ -#define _PROOF_UTILS_H_ +#ifndef PROOF_UTILS_H_ +#define PROOF_UTILS_H_ class proof_utils { public: diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp new file mode 100644 index 000000000..619f88e3b --- /dev/null +++ b/src/muz/base/rule_properties.cpp @@ -0,0 +1,208 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + rule_properties.cpp + +Abstract: + + Collect properties of rules. + +Author: + + Nikolaj Bjorner (nbjorner) 9-25-2014 + +Notes: + + +--*/ + +#include"expr_functors.h" +#include"rule_properties.h" +#include"dl_rule_set.h" +#include"for_each_expr.h" +#include"dl_context.h" + +using namespace datalog; +rule_properties::rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& p): + m(m), rm(rm), m_ctx(ctx), m_is_predicate(p), m_dt(m), m_dl(m), m_bv(m), m_generate_proof(false) {} + +rule_properties::~rule_properties() {} + +void rule_properties::collect(rule_set const& rules) { + reset(); + rule_set::iterator it = rules.begin(), end = rules.end(); + expr_sparse_mark visited; + for (; it != end; ++it) { + rule* r = *it; + m_rule = r; + unsigned ut_size = r->get_uninterpreted_tail_size(); + unsigned t_size = r->get_tail_size(); + if (r->has_negation()) { + m_negative_rules.push_back(r); + } + for (unsigned i = ut_size; i < t_size; ++i) { + for_each_expr_core(*this, visited, r->get_tail(i)); + } + if (m_generate_proof && !r->get_proof()) { + rm.mk_rule_asserted_proof(*r); + } + for (unsigned i = 0; m_inf_sort.empty() && i < r->get_decl()->get_arity(); ++i) { + sort* d = r->get_decl()->get_domain(i); + if (!m.is_bool(d) && !m_dl.is_finite_sort(d) && !m_bv.is_bv_sort(d)) { + m_inf_sort.push_back(m_rule); + } + } + } +} + +void rule_properties::check_quantifier_free() { + if (!m_quantifiers.empty()) { + rule* r = m_quantifiers.begin()->m_value; + std::stringstream stm; + stm << "cannot process quantifier in rule "; + r->display(m_ctx, stm); + throw default_exception(stm.str()); + } +} + +void rule_properties::check_for_negated_predicates() { + if (!m_negative_rules.empty()) { + rule* r = m_negative_rules[0]; + std::stringstream stm; + stm << "Rule contains negative predicate "; + r->display(m_ctx, stm); + throw default_exception(stm.str()); + } +} + + +void rule_properties::check_uninterpreted_free() { + if (!m_uninterp_funs.empty()) { + func_decl* f = m_uninterp_funs.begin()->m_key; + rule* r = m_uninterp_funs.begin()->m_value; + std::stringstream stm; + stm << "Uninterpreted '" + << f->get_name() + << "' in "; + r->display(m_ctx, stm); + throw default_exception(stm.str()); + } +} + +void rule_properties::check_infinite_sorts() { + if (!m_inf_sort.empty()) { + std::stringstream stm; + rule* r = m_inf_sort.back(); + stm << "Rule contains infinite sorts in rule "; + r->display(m_ctx, stm); + throw default_exception(stm.str()); + } +} + +void rule_properties::check_nested_free() { + if (!m_interp_pred.empty()) { + std::stringstream stm; + rule* r = m_interp_pred[0]; + stm << "Rule contains nested predicates "; + r->display(m_ctx, stm); + throw default_exception(stm.str()); + } +} + +void rule_properties::check_existential_tail() { + ast_mark visited; + ptr_vector todo, tocheck; + for (unsigned i = 0; i < m_interp_pred.size(); ++i) { + rule& r = *m_interp_pred[i]; + unsigned ut_size = r.get_uninterpreted_tail_size(); + unsigned t_size = r.get_tail_size(); + for (unsigned i = ut_size; i < t_size; ++i) { + todo.push_back(r.get_tail(i)); + } + } + context::contains_pred contains_p(m_ctx); + check_pred check_pred(contains_p, m); + + while (!todo.empty()) { + expr* e = todo.back(), *e1, *e2; + todo.pop_back(); + if (visited.is_marked(e)) { + continue; + } + visited.mark(e, true); + if (m_is_predicate(e)) { + } + else if (m.is_and(e) || m.is_or(e)) { + todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + } + else if (m.is_implies(e, e1, e2)) { + tocheck.push_back(e1); + todo.push_back(e2); + } + else if (is_quantifier(e)) { + tocheck.push_back(to_quantifier(e)->get_expr()); + } + else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && + m.is_true(e1)) { + todo.push_back(e2); + } + else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && + m.is_true(e2)) { + todo.push_back(e1); + } + else { + tocheck.push_back(e); + } + } + for (unsigned i = 0; i < tocheck.size(); ++i) { + expr* e = tocheck[i]; + if (check_pred(e)) { + std::ostringstream out; + out << "recursive predicate " << mk_ismt2_pp(e, m) << " occurs nested in the body of a rule"; + throw default_exception(out.str()); + } + } +} + + +void rule_properties::insert(ptr_vector& rules, rule* r) { + if (rules.empty() || rules.back() != r) { + rules.push_back(r); + } +} + +void rule_properties::operator()(var* n) { +} + +void rule_properties::operator()(quantifier* n) { + m_quantifiers.insert(n, m_rule); +} +void rule_properties::operator()(app* n) { + if (m_is_predicate(n)) { + insert(m_interp_pred, m_rule); + } + else if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) { + m_uninterp_funs.insert(n->get_decl(), m_rule); + } + else if (m_dt.is_accessor(n)) { + sort* s = m.get_sort(n->get_arg(0)); + SASSERT(m_dt.is_datatype(s)); + if (m_dt.get_datatype_constructors(s)->size() > 1) { + m_uninterp_funs.insert(n->get_decl(), m_rule); + } + } + else { + } + +} + +void rule_properties::reset() { + m_quantifiers.reset(); + m_uninterp_funs.reset(); + m_interp_pred.reset(); + m_negative_rules.reset(); + m_inf_sort.reset(); +} + diff --git a/src/muz/base/rule_properties.h b/src/muz/base/rule_properties.h new file mode 100644 index 000000000..f53ad3841 --- /dev/null +++ b/src/muz/base/rule_properties.h @@ -0,0 +1,65 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + rule_properties.h + +Abstract: + + Collect properties of rules. + +Author: + + Nikolaj Bjorner (nbjorner) 9-25-2014 + +Notes: + + +--*/ + +#ifndef RULE_PROPERTIES_H_ +#define RULE_PROPERTIES_H_ + +#include"ast.h" +#include"datatype_decl_plugin.h" +#include"bv_decl_plugin.h" +#include"dl_rule.h" + +namespace datalog { + class rule_properties { + ast_manager& m; + rule_manager& rm; + context& m_ctx; + i_expr_pred& m_is_predicate; + datatype_util m_dt; + dl_decl_util m_dl; + bv_util m_bv; + bool m_generate_proof; + rule* m_rule; + obj_map m_quantifiers; + obj_map m_uninterp_funs; + ptr_vector m_interp_pred; + ptr_vector m_negative_rules; + ptr_vector m_inf_sort; + + void insert(ptr_vector& rules, rule* r); + public: + rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& is_predicate); + ~rule_properties(); + void set_generate_proof(bool generate_proof) { m_generate_proof = generate_proof; } + void collect(rule_set const& r); + void check_quantifier_free(); + void check_uninterpreted_free(); + void check_existential_tail(); + void check_for_negated_predicates(); + void check_nested_free(); + void check_infinite_sorts(); + void operator()(var* n); + void operator()(quantifier* n); + void operator()(app* n); + void reset(); + }; +} + +#endif /* RULE_PROPERTIES_H_ */ diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 51b1a5295..221832fed 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -32,7 +32,6 @@ Revision History: #include "dl_transforms.h" #include "dl_mk_rule_inliner.h" #include "scoped_proof.h" -#include"fixedpoint_params.hpp" namespace datalog { @@ -297,7 +296,7 @@ namespace datalog { vector substs; expr_ref fml(m), concl(m); - r->to_formula(fml); + rm.to_formula(*r, fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); proof_ref p(m); @@ -307,7 +306,7 @@ namespace datalog { expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); - r1->to_formula(concl); + rm.to_formula(*r1.get(), concl); scoped_proof _sp(m); p = r->get_proof(); @@ -324,7 +323,7 @@ namespace datalog { r0 = r1; } else { - r2->to_formula(concl); + rm.to_formula(*r, concl); scoped_proof _sp(m); p = r->get_proof(); if (!p) { @@ -488,7 +487,7 @@ namespace datalog { return proof_ref(0, m); } TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); - + rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref prop_r(m), prop_v(m), fml(m), prop_body(m), tmp(m), body(m); expr_ref_vector args(m); proof_ref_vector prs(m); @@ -508,7 +507,7 @@ namespace datalog { } } SASSERT(r); - r->to_formula(fml); + rm.to_formula(*r, fml); IF_VERBOSE(1, verbose_stream() << mk_pp(fml, m) << "\n";); prs.push_back(r->get_proof()); unsigned sz = r->get_uninterpreted_tail_size(); @@ -624,11 +623,12 @@ namespace datalog { } expr_ref bind_vars(expr* e, expr* pat) { - ptr_vector vars, sorts; + ptr_vector sorts; svector names; expr_ref_vector binding(m), patterns(m); expr_ref tmp(m), head(m); - get_free_vars(e, vars); + expr_free_vars vars; + vars(e); for (unsigned i = 0; i < vars.size(); ++i) { if (vars[i]) { binding.push_back(m.mk_var(sorts.size(), vars[i])); @@ -1028,6 +1028,7 @@ namespace datalog { proof_ref get_proof(model_ref& md, app* trace, app* path) { datatype_util dtu(m); + rule_manager& rm = b.m_ctx.get_rule_manager(); sort* trace_sort = m.get_sort(trace); func_decl* p = m_sort2pred.find(trace_sort); datalog::rule_vector const& rules = b.m_rules.get_predicate_rules(p); @@ -1046,7 +1047,7 @@ namespace datalog { var_subst vs(m, false); mk_subst(*rules[i], path, trace, sub); - rules[i]->to_formula(fml); + rm.to_formula(*rules[i], fml); prs.push_back(rules[i]->get_proof()); unsigned sz = trace->get_num_args(); if (sub.empty() && sz == 0) { @@ -1219,7 +1220,7 @@ namespace datalog { vector substs; expr_ref fml(m), concl(m); - r->to_formula(fml); + rm.to_formula(*r, fml); r2 = r; rm.substitute(r2, sub.size(), sub.c_ptr()); proof_ref p(m); @@ -1237,7 +1238,7 @@ namespace datalog { expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); apply_subst(sub, sub2); unifier.apply(*r0.get(), 0, *r2.get(), r1); - r1->to_formula(concl); + rm.to_formula(*r1.get(), concl); scoped_proof _sp(m); proof* premises[2] = { pr, p }; @@ -1250,7 +1251,7 @@ namespace datalog { r0 = r1; } else { - r2->to_formula(concl); + rm.to_formula(*r2.get(), concl); scoped_proof _sp(m); if (sub.empty()) { pr = p; @@ -1437,23 +1438,26 @@ namespace datalog { m_ctx.ensure_opened(); m_rules.reset(); datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); - datalog::rule_set old_rules(m_ctx.get_rules()); - rule_manager.mk_query(query, m_ctx.get_rules()); + rule_set& rules0 = m_ctx.get_rules(); + datalog::rule_set old_rules(rules0); + rule_manager.mk_query(query, rules0); expr_ref bg_assertion = m_ctx.get_background_assertion(); apply_default_transformation(m_ctx); - if (m_ctx.get_params().slice()) { + if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); m_ctx.transform_rules(transformer); } - if (m_ctx.get_rules().get_output_predicates().empty()) { + + const rule_set& rules = m_ctx.get_rules(); + if (rules.get_output_predicates().empty()) { return l_false; } - m_query_pred = m_ctx.get_rules().get_output_predicate(); - m_rules.replace_rules(m_ctx.get_rules()); + m_query_pred = rules.get_output_predicate(); + m_rules.replace_rules(rules); m_rules.close(); m_ctx.reopen(); m_ctx.replace_rules(old_rules); @@ -1498,7 +1502,7 @@ namespace datalog { if (m_rules.get_rule(i)->get_uninterpreted_tail_size() > 1) { return false; } - if (m_rules.get_rule(i)->has_quantifiers()) { + if (m_rules.get_rule_manager().has_quantifiers(*m_rules.get_rule(i))) { return false; } } diff --git a/src/muz/bmc/dl_bmc_engine.h b/src/muz/bmc/dl_bmc_engine.h index e20987002..b9b161753 100644 --- a/src/muz/bmc/dl_bmc_engine.h +++ b/src/muz/bmc/dl_bmc_engine.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _DL_BMC_ENGINE_H_ -#define _DL_BMC_ENGINE_H_ +#ifndef DL_BMC_ENGINE_H_ +#define DL_BMC_ENGINE_H_ #include "params.h" #include "statistics.h" diff --git a/src/muz/clp/clp_context.cpp b/src/muz/clp/clp_context.cpp index 11f6ae1db..acc38f9e6 100644 --- a/src/muz/clp/clp_context.cpp +++ b/src/muz/clp/clp_context.cpp @@ -70,11 +70,11 @@ namespace datalog { m_goals.reset(); rm.mk_query(query, m_ctx.get_rules()); apply_default_transformation(m_ctx); - if (m_ctx.get_rules().get_output_predicates().empty()) { + const rule_set& rules = m_ctx.get_rules(); + if (rules.get_output_predicates().empty()) { return l_false; } - func_decl* head_decl = m_ctx.get_rules().get_output_predicate(); - rule_set& rules = m_ctx.get_rules(); + func_decl *head_decl = rules.get_output_predicate(); rule_vector const& rv = rules.get_predicate_rules(head_decl); if (rv.empty()) { return l_false; @@ -123,14 +123,14 @@ namespace datalog { } void ground(expr_ref& e) { - ptr_vector sorts; - get_free_vars(e, sorts); - if (m_ground.size() < sorts.size()) { - m_ground.resize(sorts.size()); + expr_free_vars fv; + fv(e); + if (m_ground.size() < fv.size()) { + m_ground.resize(fv.size()); } - for (unsigned i = 0; i < sorts.size(); ++i) { - if (sorts[i] && !m_ground[i].get()) { - m_ground[i] = m.mk_fresh_const("c",sorts[i]); + for (unsigned i = 0; i < fv.size(); ++i) { + if (fv[i] && !m_ground[i].get()) { + m_ground[i] = m.mk_fresh_const("c", fv[i]); } } m_var_subst(e, m_ground.size(), m_ground.c_ptr(), e); diff --git a/src/muz/clp/clp_context.h b/src/muz/clp/clp_context.h index 6464ae634..de0b25b7f 100644 --- a/src/muz/clp/clp_context.h +++ b/src/muz/clp/clp_context.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _CLP_CONTEXT_H_ -#define _CLP_CONTEXT_H_ +#ifndef CLP_CONTEXT_H_ +#define CLP_CONTEXT_H_ #include "ast.h" #include "lbool.h" diff --git a/src/muz/dataflow/dataflow.cpp b/src/muz/dataflow/dataflow.cpp new file mode 100644 index 000000000..73e152ad7 --- /dev/null +++ b/src/muz/dataflow/dataflow.cpp @@ -0,0 +1,24 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + dataflow.cpp + +Abstract: + + Generic bottom-up and top-down data-flow engine for analysis + of rule sets. + +Author: + Henning Guenther (t-hennig) + +--*/ + +#include "dataflow.h" +#include "reachability.h" + +namespace datalog { + + const reachability_info reachability_info::null_fact; +} diff --git a/src/muz/dataflow/dataflow.h b/src/muz/dataflow/dataflow.h new file mode 100644 index 000000000..9e100121a --- /dev/null +++ b/src/muz/dataflow/dataflow.h @@ -0,0 +1,267 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + dataflow.h + +Abstract: + + Generic bottom-up and top-down data-flow engine for analysis + of rule sets. + +Author: + Henning Guenther (t-hennig) + +--*/ + +#ifndef DATAFLOW_H_ +#define DATAFLOW_H_ + +#include "dl_rule.h" +#include "dl_rule_set.h" +#include "hashtable.h" +#include "vector.h" + +namespace datalog { + template class fact_reader; + template class fact_writer; + /* The structure of fact classes: + class fact { + public: + typedef ... ctx_t; + // Empty fact + static fact null_fact; + fact(); -- bottom + // Init (Top down) + void init_down(ctx_t& ctx, const rule* r); + // Init (Bottom up) + bool init_up(ctx_t& ctx, const rule* r); + // Step (Bottom up) + bool propagate_up(ctx_t& ctx, const rule* r, const fact_reader& tail_facts); + // Step (Top down) + void propagate_down(ctx_t& ctx, const rule* r, fact_writer& tail_facts) const; + // Debugging + void dump(ctx_t& ctx, std::ostream& outp) const; + // Union + void join(ctx_t& ctx, const Fact& oth); + // Intersection + void intersect(ctx_t& ctx, const Fact& oth); + }; */ + template class dataflow_engine { + public: + typedef map, ptr_eq > fact_db; + typedef hashtable, ptr_eq > todo_set; + typedef typename fact_db::iterator iterator; + private: + const rule_set& m_rules; + fact_db m_facts; + todo_set m_todo[2]; + unsigned m_todo_idx; + typename Fact::ctx_t& m_context; + rule_set::decl2rules m_body2rules; + + void init_bottom_up() { + for (rule_set::iterator it = m_rules.begin(); it != m_rules.end(); ++it) { + rule* cur = *it; + for (unsigned i = 0; i < cur->get_uninterpreted_tail_size(); ++i) { + func_decl *d = cur->get_decl(i); + rule_set::decl2rules::obj_map_entry *e = m_body2rules.insert_if_not_there2(d, 0); + if (!e->get_data().m_value) { + e->get_data().m_value = alloc(ptr_vector); + } + e->get_data().m_value->push_back(cur); + } + if (cur->get_uninterpreted_tail_size() == 0) { + func_decl *sym = cur->get_head()->get_decl(); + bool new_info = m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value.init_up(m_context, cur); + if (new_info) { + m_todo[m_todo_idx].insert(sym); + } + } + } + } + + void init_top_down() { + const func_decl_set& output_preds = m_rules.get_output_predicates(); + for (func_decl_set::iterator I = output_preds.begin(), + E = output_preds.end(); I != E; ++I) { + func_decl* sym = *I; + const rule_vector& output_rules = m_rules.get_predicate_rules(sym); + for (unsigned i = 0; i < output_rules.size(); ++i) { + m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value.init_down(m_context, output_rules[i]); + m_todo[m_todo_idx].insert(sym); + } + } + } + + void step_bottom_up() { + for(todo_set::iterator I = m_todo[m_todo_idx].begin(), + E = m_todo[m_todo_idx].end(); I!=E; ++I) { + ptr_vector * rules; + if (!m_body2rules.find(*I, rules)) + continue; + + for (ptr_vector::iterator I2 = rules->begin(), + E2 = rules->end(); I2 != E2; ++I2) { + func_decl* head_sym = (*I2)->get_head()->get_decl(); + fact_reader tail_facts(m_facts, *I2); + bool new_info = m_facts.insert_if_not_there2(head_sym, Fact())->get_data().m_value.propagate_up(m_context, *I2, tail_facts); + if (new_info) { + m_todo[!m_todo_idx].insert(head_sym); + } + } + } + // Update todo list + m_todo[m_todo_idx].reset(); + m_todo_idx = !m_todo_idx; + } + + void step_top_down() { + for(todo_set::iterator I = m_todo[m_todo_idx].begin(), + E = m_todo[m_todo_idx].end(); I!=E; ++I) { + func_decl* head_sym = *I; + // We can't use a reference here because we are changing the map while using the reference + const Fact head_fact = m_facts.get(head_sym, Fact::null_fact); + const rule_vector& deps = m_rules.get_predicate_rules(head_sym); + const unsigned deps_size = deps.size(); + for (unsigned i = 0; i < deps_size; ++i) { + rule *trg_rule = deps[i]; + fact_writer writer(m_facts, trg_rule, m_todo[!m_todo_idx]); + // Generate new facts + head_fact.propagate_down(m_context, trg_rule, writer); + } + } + // Update todo list + m_todo[m_todo_idx].reset(); + m_todo_idx = !m_todo_idx; + } + + bool done() const { + return m_todo[m_todo_idx].empty(); + } + + public: + dataflow_engine(typename Fact::ctx_t& ctx, const rule_set& rules) : m_rules(rules), m_todo_idx(0), m_context(ctx) {} + + ~dataflow_engine() { + for (rule_set::decl2rules::iterator it = m_body2rules.begin(); it != m_body2rules.end(); ++it) { + dealloc(it->m_value); + } + } + + void dump(std::ostream& outp) { + obj_hashtable visited; + for (rule_set::iterator I = m_rules.begin(), + E = m_rules.end(); I != E; ++I) { + const rule* r = *I; + func_decl* head_decl = r->get_decl(); + obj_hashtable::entry *dummy; + if (visited.insert_if_not_there_core(head_decl, dummy)) { + const Fact& fact = m_facts.get(head_decl, Fact::null_fact); + outp << head_decl->get_name() << " -> "; + fact.dump(m_context, outp); + outp << "\n"; + } + for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { + func_decl *tail_decl = r->get_decl(i); + if (visited.insert_if_not_there_core(tail_decl, dummy)) { + const Fact& fact = m_facts.get(tail_decl, Fact::null_fact); + outp << tail_decl->get_name() << " -> "; + fact.dump(m_context, outp); + outp << "\n"; + } + } + } + } + + void run_bottom_up() { + init_bottom_up(); + while (!done()) step_bottom_up(); + } + + void run_top_down() { + init_top_down(); + while (!done()) step_top_down(); + } + + const Fact& get_fact(func_decl* decl) const { + return m_facts.get(decl, Fact::null_fact); + } + + iterator begin() const { return m_facts.begin(); } + iterator end() const { return m_facts.end(); } + + void join(const dataflow_engine& oth) { + for (typename fact_db::iterator I = oth.m_facts.begin(), + E = oth.m_facts.end(); I != E; ++I) { + typename fact_db::entry* entry; + if (m_facts.insert_if_not_there_core(I->m_key, entry)) { + entry->get_data().m_value = I->m_value; + } else { + entry->get_data().m_value.join(m_context, I->m_value); + } + } + } + + void intersect(const dataflow_engine& oth) { + vector to_delete; + for (typename fact_db::iterator I = m_facts.begin(), + E = m_facts.end(); I != E; ++I) { + + if (typename fact_db::entry *entry = oth.m_facts.find_core(I->m_key)) { + I->m_value.intersect(m_context, entry->get_data().m_value); + } else { + to_delete.push_back(I->m_key); + } + } + for (unsigned i = 0; i < to_delete.size(); ++i) { + m_facts.erase(to_delete[i]); + } + } + }; + + // This helper-class is used to look up facts for rule tails + template class fact_reader { + typedef typename dataflow_engine::fact_db fact_db; + const fact_db& m_facts; + const rule* m_rule; + public: + fact_reader(const fact_db& facts, const rule* r) + : m_facts(facts), m_rule(r) { + + } + const Fact& get(unsigned idx) const { + return m_facts.get(m_rule->get_decl(idx), Fact::null_fact); + } + unsigned size() const { + return m_rule->get_uninterpreted_tail_size(); + } + }; + + template class fact_writer { + friend class dataflow_engine; + typedef typename dataflow_engine::fact_db fact_db; + fact_db& m_facts; + const rule* m_rule; + typename dataflow_engine::todo_set& m_todo; + public: + fact_writer(fact_db& facts, const rule* r, typename dataflow_engine::todo_set& todo) + : m_facts(facts), m_rule(r), m_todo(todo) {} + + Fact& get(unsigned idx) { + func_decl *sym = m_rule->get_decl(idx); + return m_facts.insert_if_not_there2(sym, Fact())->get_data().m_value; + } + + void set_changed(unsigned idx) { + m_todo.insert(m_rule->get_decl(idx)); + } + + unsigned size() const { + return m_rule->get_uninterpreted_tail_size(); + } + }; +} + +#endif diff --git a/src/muz/dataflow/reachability.h b/src/muz/dataflow/reachability.h new file mode 100644 index 000000000..54da04967 --- /dev/null +++ b/src/muz/dataflow/reachability.h @@ -0,0 +1,82 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + reachability.h + +Abstract: + + Abstract domain for tracking rule reachability. + +Author: + Henning Guenther (t-hennig) + +--*/ + +#ifndef REACHABILITY_H_ +#define REACHABILITY_H_ + +#include "dataflow.h" + +namespace datalog { + class reachability_info { + bool m_reachable; + reachability_info(bool r) : m_reachable(r) {} + public: + typedef ast_manager ctx_t; + static const reachability_info null_fact; + reachability_info() : m_reachable(false) {} + + void init_down(const ctx_t& m, const rule* r) { + m_reachable = true; + } + + bool init_up(const ctx_t& m, const rule* r) { + if (m_reachable) + return false; + else { + m_reachable = true; + return true; + } + } + + void propagate_down(const ctx_t& manager, const rule* r, fact_writer& tail_facts) const { + SASSERT(m_reachable); + for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { + reachability_info& tail_fact = tail_facts.get(i); + if (!tail_fact.m_reachable) { + tail_fact.m_reachable = true; + tail_facts.set_changed(i); + } + } + } + + bool propagate_up(const ctx_t& manager, const rule* r, const fact_reader& tail_facts) { + if (m_reachable) + return false; + + for (unsigned i = 0; i < r->get_positive_tail_size(); ++i) { + if (!tail_facts.get(i).m_reachable) { + return false; + } + } + m_reachable = true; + return true; + } + + void join(const ctx_t& manager, const reachability_info& oth) { + m_reachable |= oth.m_reachable; + } + + void dump(const ctx_t& manager, std::ostream& outp) const { + outp << (m_reachable ? "reachable" : "unreachable"); + } + + bool is_reachable() const { return m_reachable; } + }; + + typedef dataflow_engine reachability; +} + +#endif diff --git a/src/muz/ddnf/ddnf.cpp b/src/muz/ddnf/ddnf.cpp new file mode 100644 index 000000000..6545d4282 --- /dev/null +++ b/src/muz/ddnf/ddnf.cpp @@ -0,0 +1,905 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + ddnf.cpp + +Abstract: + + DDNF based engine. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-08-21 + +Revision History: + + - inherits from Nuno Lopes Hassel utilities + and Garvit Juniwal's DDNF engine. + +--*/ + +#include "ddnf.h" +#include "dl_rule_set.h" +#include "dl_context.h" +#include "scoped_proof.h" +#include "bv_decl_plugin.h" +#include "tbv.h" + +namespace datalog { + + class ddnf_mgr; + class ddnf_node; + typedef ref_vector ddnf_node_vector; + + class ddnf_node { + public: + + struct eq { + tbv_manager& m; + eq(tbv_manager& m):m(m) {} + bool operator()(ddnf_node* n1, ddnf_node* n2) const { + return m.equals(n1->get_tbv(), n2->get_tbv()); + } + }; + + struct hash { + tbv_manager& m; + hash(tbv_manager& m):m(m) {} + unsigned operator()(ddnf_node* n) const { + return m.hash(n->get_tbv()); + } + }; + + typedef ptr_hashtable ddnf_nodes; + private: + tbv_manager& tbvm; + tbv const& m_tbv; + ddnf_node_vector m_children; + unsigned m_refs; + unsigned m_id; + ddnf_node::hash m_hash; + ddnf_node::eq m_eq; + ddnf_nodes m_descendants; + + friend class ddnf_mgr; + + public: + ddnf_node(ddnf_mgr& m, tbv_manager& tbvm, tbv const& tbv, unsigned id): + tbvm(tbvm), + m_tbv(tbv), + m_children(m), + m_refs(0), + m_id(id), + m_hash(tbvm), + m_eq(tbvm), + m_descendants(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) { + } + + ~ddnf_node() {} + + unsigned inc_ref() { + return ++m_refs; + } + + void dec_ref() { + SASSERT(m_refs > 0); + --m_refs; + if (m_refs == 0) { + dealloc(this); + } + } + + ddnf_nodes& descendants() { return m_descendants; } + + unsigned get_id() const { return m_id; } + + unsigned num_children() const { return m_children.size(); } + + ddnf_node* operator[](unsigned index) { return m_children[index].get(); } + + tbv const& get_tbv() const { return m_tbv; } + + void add_child(ddnf_node* n); + + void remove_child(ddnf_node* n); + + bool contains_child(ddnf_node* n) const; + + void display(std::ostream& out) const { + out << "node[" << get_id() << ": "; + tbvm.display(out, m_tbv); + for (unsigned i = 0; i < m_children.size(); ++i) { + out << " " << m_children[i]->get_id(); + } + out << "]"; + } + }; + + typedef ddnf_node::ddnf_nodes ddnf_nodes; + + + class ddnf_mgr { + struct stats { + unsigned m_num_inserts; + unsigned m_num_comparisons; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + ddnf_node* m_root; + ddnf_node_vector m_noderefs; + bool m_internalized; + tbv_manager m_tbv; + ddnf_node::hash m_hash; + ddnf_node::eq m_eq; + ddnf_nodes m_nodes; + svector m_marked; + stats m_stats; + public: + ddnf_mgr(unsigned n): m_noderefs(*this), m_internalized(false), m_tbv(n), + m_hash(m_tbv), m_eq(m_tbv), + m_nodes(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_hash, m_eq) { + tbv* bX = m_tbv.allocateX(); + m_root = alloc(ddnf_node, *this, m_tbv, *bX, m_nodes.size()); + m_noderefs.push_back(m_root); + m_nodes.insert(m_root); + } + + ~ddnf_mgr() { + m_noderefs.reset(); + m_tbv.reset(); + } + + void inc_ref(ddnf_node* n) { + n->inc_ref(); + } + + void dec_ref(ddnf_node* n) { + n->dec_ref(); + } + + void reset_accumulate() { + m_marked.resize(m_nodes.size()); + for (unsigned i = 0; i < m_marked.size(); ++i) { + m_marked[i] = false; + } + } + + void accumulate(tbv const& t, unsigned_vector& acc) { + ddnf_node* n = find(t); + ptr_vector todo; + todo.push_back(n); + while (!todo.empty()) { + n = todo.back(); + todo.pop_back(); + unsigned id = n->get_id(); + if (m_marked[id]) continue; + acc.push_back(id); + m_marked[id] = true; + unsigned sz = n->num_children(); + for (unsigned i = 0; i < sz; ++i) { + todo.push_back((*n)[i]); + } + } + } + + ddnf_node* insert(tbv const& t) { + SASSERT(!m_internalized); + ptr_vector new_tbvs; + new_tbvs.push_back(&t); + for (unsigned i = 0; i < new_tbvs.size(); ++i) { + tbv const& nt = *new_tbvs[i]; + IF_VERBOSE(10, m_tbv.display(verbose_stream() << "insert: ", nt); verbose_stream() << "\n";); + if (contains(nt)) continue; + ddnf_node* n = alloc(ddnf_node, *this, m_tbv, nt, m_noderefs.size()); + m_noderefs.push_back(n); + m_nodes.insert(n); + insert(*m_root, n, new_tbvs); + } + return find(t); + } + + tbv* allocate(uint64 v, unsigned hi, unsigned lo) { + return m_tbv.allocate(v, hi, lo); + } + + tbv_manager& get_tbv_manager() { + return m_tbv; + } + + unsigned size() const { + return m_noderefs.size(); + } + + ddnf_nodes const& lookup(tbv const& t) { + internalize(); + return find(t)->descendants(); + } + + void display_statistics(std::ostream& out) const { + std::cout << "Number of insertions: " << m_stats.m_num_inserts << "\n"; + std::cout << "Number of comparisons: " << m_stats.m_num_comparisons << "\n"; + std::cout << "Number of nodes: " << size() << "\n"; + } + + void display(std::ostream& out) const { + for (unsigned i = 0; i < m_noderefs.size(); ++i) { + m_noderefs[i]->display(out); + out << "\n"; + } + } + + bool contains(tbv const& t) { + ddnf_node dummy(*this, m_tbv, t, 0); + return m_nodes.contains(&dummy); + } + + bool well_formed() { + ptr_vector todo; + todo.push_back(m_root); + reset_accumulate(); + while (!todo.empty()) { + ddnf_node* n = todo.back(); + todo.pop_back(); + if (m_marked[n->get_id()]) continue; + m_marked[n->get_id()] = true; + unsigned sz = n->num_children(); + for (unsigned i = 0; i < sz; ++i) { + ddnf_node* child = (*n)[i]; + if (!m_tbv.contains(n->get_tbv(), child->get_tbv())) { + IF_VERBOSE(0, + m_tbv.display(verbose_stream() << "parent ", n->get_tbv()); + m_tbv.display(verbose_stream() << " does not contains child: ", child->get_tbv()); + display(verbose_stream()); + ); + return false; + } + todo.push_back(child); + } + + } + return true; + } + + + private: + + ddnf_node* find(tbv const& t) { + ddnf_node dummy(*this, m_tbv, t, 0); + return *(m_nodes.find(&dummy)); + } + + + void insert(ddnf_node& root, ddnf_node* new_n, ptr_vector& new_intersections) { + tbv const& new_tbv = new_n->get_tbv(); + + SASSERT(m_tbv.contains(root.get_tbv(), new_tbv)); + if (&root == new_n) return; + ++m_stats.m_num_inserts; + bool inserted = false; + for (unsigned i = 0; i < root.num_children(); ++i) { + ddnf_node& child = *(root[i]); + ++m_stats.m_num_comparisons; + if (m_tbv.contains(child.get_tbv(), new_tbv)) { + inserted = true; + insert(child, new_n, new_intersections); + } + } + if (inserted) { + return; + } + ddnf_node_vector subset_children(*this); + tbv* intr = m_tbv.allocate(); + for (unsigned i = 0; i < root.num_children(); ++i) { + ddnf_node& child = *(root[i]); + // cannot be superset + SASSERT(!m_tbv.contains(child.get_tbv(),new_tbv)); + // checking for subset + if (m_tbv.contains(new_tbv, child.get_tbv())) { + subset_children.push_back(&child); + ++m_stats.m_num_comparisons; + } + else if (m_tbv.intersect(child.get_tbv(), new_tbv, *intr)) { + // this means there is a non-full intersection + new_intersections.push_back(intr); + intr = m_tbv.allocate(); + m_stats.m_num_comparisons += 2; + } + else { + m_stats.m_num_comparisons += 2; + } + } + m_tbv.deallocate(intr); + for (unsigned i = 0; i < subset_children.size(); ++i) { + root.remove_child(subset_children[i].get()); + new_n->add_child(subset_children[i].get()); + } + root.add_child(new_n); + } + + + void internalize() { + // populate maps (should be bit-sets) of decendants. + if (m_internalized) { + return; + } + ptr_vector todo; + todo.push_back(m_root); + svector done(m_noderefs.size(), false); + while (!todo.empty()) { + ddnf_node& n = *todo.back(); + if (done[n.get_id()]) { + todo.pop_back(); + continue; + } + unsigned sz = n.num_children(); + bool all_done = true; + for (unsigned i = 0; i < sz; ++i) { + ddnf_node* child = n[i]; + if (!done[child->get_id()]) { + all_done = false; + todo.push_back(child); + } + } + if (all_done) { + n.descendants().insert(&n); + for (unsigned i = 0; i < sz; ++i) { + ddnf_node* child = n[i]; + add_table(n.descendants(), child->descendants()); + } + done[n.get_id()] = true; + todo.pop_back(); + } + } + m_internalized = true; + } + + void add_table(ddnf_nodes& dst, ddnf_nodes const& src) { + ddnf_nodes::iterator it = src.begin(), end = src.end(); + for (; it != end; ++it) { + dst.insert(*it); + } + } + }; + + ddnf_core::ddnf_core(unsigned n) { + m_imp = alloc(ddnf_mgr, n); + } + ddnf_core::~ddnf_core() { + dealloc(m_imp); + } + ddnf_node* ddnf_core::insert(tbv const& t) { + return m_imp->insert(t); + } + tbv_manager& ddnf_core::get_tbv_manager() { + return m_imp->get_tbv_manager(); + } + unsigned ddnf_core::size() const { + return m_imp->size(); + } + bool ddnf_core::contains(tbv const& t) { + return m_imp->contains(t); + } + bool ddnf_core::well_formed() { + return m_imp->well_formed(); + } + + void ddnf_core::reset_accumulate() { + return m_imp->reset_accumulate(); + } + void ddnf_core::accumulate(tbv const& t, unsigned_vector& acc) { + return m_imp->accumulate(t, acc); + } + void ddnf_core::display(std::ostream& out) const { + m_imp->display(out); + } + void ddnf_core::display_statistics(std::ostream& out) const { + m_imp->display_statistics(out); + } + + + void ddnf_node::add_child(ddnf_node* n) { + //SASSERT(!m_tbv.is_subset(n->m_tbv)); + m_children.push_back(n); + } + + void ddnf_node::remove_child(ddnf_node* n) { + m_children.erase(n); + } + + bool ddnf_node::contains_child(ddnf_node* n) const { + return m_children.contains(n); + } + + + class ddnfs { + u_map m_mgrs; + public: + ddnfs() {} + + ~ddnfs() { + u_map::iterator it = m_mgrs.begin(), end = m_mgrs.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + } + + tbv* allocate(unsigned num_bits, uint64 v, unsigned hi, unsigned lo) { + return get(num_bits).allocate(v, hi, lo); + } + void insert(unsigned num_bits, tbv const& t) { + get(num_bits).insert(t); + } + + ddnf_mgr& get(unsigned num_bits) { + return *insert(num_bits); + } + + ddnf_nodes const& lookup(unsigned n, tbv const& t) const { + return m_mgrs.find(n)->lookup(t); + } + + void display(std::ostream& out) const { + u_map::iterator it = m_mgrs.begin(), end = m_mgrs.end(); + for (; it != end; ++it) { + it->m_value->display(out); + } + } + + private: + + ddnf_mgr* insert(unsigned n) { + ddnf_mgr* m = 0; + if (!m_mgrs.find(n, m)) { + m = alloc(ddnf_mgr, n); + m_mgrs.insert(n, m); + } + return m; + } + }; + + class ddnf::imp { + struct stats { + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + context& m_ctx; + ast_manager& m; + rule_manager& rm; + bv_util bv; + volatile bool m_cancel; + ptr_vector m_todo; + ast_mark m_visited1, m_visited2; + ddnfs m_ddnfs; + stats m_stats; + obj_map m_expr2tbv; + obj_map m_cache; + expr_ref_vector m_trail; + context m_inner_ctx; + + public: + imp(context& ctx): + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + bv(m), + m_cancel(false), + m_trail(m), + m_inner_ctx(m, m_ctx.get_register_engine(), m_ctx.get_fparams()) + { + params_ref params; + params.set_sym("engine", symbol("datalog")); + m_inner_ctx.updt_params(params); + } + + ~imp() {} + + lbool query(expr* query) { + m_ctx.ensure_opened(); + rule_set& old_rules = m_ctx.get_rules(); + rm.mk_query(query, old_rules); + rule_set new_rules(m_ctx); + IF_VERBOSE(10, verbose_stream() << "(ddnf.preprocess)\n";); + if (!pre_process_rules(old_rules)) { + return l_undef; + } + IF_VERBOSE(10, verbose_stream() << "(ddnf.compile)\n";); + if (!compile_rules1(old_rules, new_rules)) { + return l_undef; + } + IF_VERBOSE(15, m_ddnfs.display(verbose_stream());); + + dump_rules(new_rules); + return l_undef; + + // return execute_rules(new_rules); + } + + void cancel() { + m_cancel = true; + m_inner_ctx.cancel(); + } + + void cleanup() { + m_cancel = false; + } + + void reset_statistics() { + m_stats.reset(); + } + + void collect_statistics(statistics& st) const { + } + + void display_certificate(std::ostream& out) const { + expr_ref ans = get_answer(); + out << mk_pp(ans, m) << "\n"; + } + + expr_ref get_answer() const { + UNREACHABLE(); + return expr_ref(m.mk_true(), m); + } + + private: + + proof_ref get_proof() const { + scoped_proof sp(m); + proof_ref pr(m); + return pr; + } + + bool pre_process_rules(rule_set const& rules) { + m_visited1.reset(); + m_todo.reset(); + m_cache.reset(); + m_expr2tbv.reset(); + datalog::rule_set::iterator it = rules.begin(); + datalog::rule_set::iterator end = rules.end(); + for (; it != end; ++it) { + if (!pre_process_rule(**it)) { + return false; + } + } + return true; + } + + bool pre_process_rule(rule const& r) { + // all predicates are monadic. + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned sz = r.get_tail_size(); + for (unsigned i = utsz; i < sz; ++i) { + m_todo.push_back(r.get_tail(i)); + } + if (process_todo()) { + return true; + } + else { + r.display(m_ctx, std::cout); + return false; + } + } + + bool process_todo() { + while (!m_todo.empty()) { + expr* e = m_todo.back(); + m_todo.pop_back(); + if (m_visited1.is_marked(e)) { + continue; + } + m_visited1.mark(e, true); + if (is_var(e)) { + continue; + } + if (is_quantifier(e)) { + return false; + } + if (m.is_and(e) || + m.is_or(e) || + m.is_iff(e) || + m.is_not(e) || + m.is_implies(e)) { + m_todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + continue; + } + if (is_ground(e)) { + continue; + } + if (process_atomic(e)) { + continue; + } + IF_VERBOSE(0, verbose_stream() << "Could not handle: " << mk_pp(e, m) << "\n";); + return false; + } + return true; + } + + bool process_atomic(expr* e) { + expr* e1, *e2, *e3; + unsigned lo, hi; + + if (m.is_eq(e, e1, e2) && bv.is_bv(e1)) { + if (is_var(e1) && is_ground(e2)) { + return process_eq(e, to_var(e1), bv.get_bv_size(e1)-1, 0, e2); + } + if (is_var(e2) && is_ground(e1)) { + return process_eq(e, to_var(e2), bv.get_bv_size(e2)-1, 0, e1); + } + if (bv.is_extract(e1, lo, hi, e3) && is_var(e3) && is_ground(e2)) { + return process_eq(e, to_var(e3), hi, lo, e2); + } + if (bv.is_extract(e2, lo, hi, e3) && is_var(e3) && is_ground(e1)) { + return process_eq(e, to_var(e3), hi, lo, e1); + } + if (is_var(e1) && is_var(e2)) { + return true; + } + } + return false; + } + + bool process_eq(expr* e, var* v, unsigned hi, unsigned lo, expr* c) { + rational val; + unsigned sz_c; + unsigned sz_v = bv.get_bv_size(v); + if (!bv.is_numeral(c, val, sz_c)) { + return false; + } + if (!val.is_uint64()) { + return false; + } + // v[hi:lo] = val + tbv* tv = m_ddnfs.allocate(sz_v, val.get_uint64(), hi, lo); + m_ddnfs.insert(sz_v, *tv); + m_expr2tbv.insert(e, tv); + // std::cout << mk_pp(v, m) << " " << lo << " " << hi << " " << v << " " << tbv << "\n"; + return true; + } + + void init_ctx(rule_set& rules) { + m_inner_ctx.reset(); + func_decl_set const& predicates = m_ctx.get_predicates(); + for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) { + m_inner_ctx.register_predicate(*fit, false); + } + m_inner_ctx.ensure_opened(); + m_inner_ctx.replace_rules(rules); + m_inner_ctx.close(); + } + + void dump_rules(rule_set& rules) { + init_ctx(rules); + m_inner_ctx.display_smt2(0, 0, std::cout); + } + + lbool execute_rules(rule_set& rules) { + init_ctx(rules); + + ptr_vector heads; + rule_set::decl2rules::iterator dit = rules.begin_grouped_rules(); + rule_set::decl2rules::iterator dend = rules.end_grouped_rules(); + for (; dit != dend; ++dit) { + heads.push_back(dit->m_key); + } + return m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); + } + + bool compile_rules1(rule_set const& rules, rule_set& new_rules) { + datalog::rule_set::iterator it = rules.begin(); + datalog::rule_set::iterator end = rules.end(); + unsigned idx = 0; + for (; it != end; ++idx, ++it) { + if (!compile_rule1(**it, rules, new_rules)) { + return false; + } + } + return true; + } + + bool compile_rule1(rule& r, rule_set const& old_rules, rule_set& new_rules) { + app_ref head(m), pred(m); + app_ref_vector body(m); + expr_ref tmp(m); + compile_predicate(r.get_head(), head); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned sz = r.get_tail_size(); + for (unsigned i = 0; i < utsz; ++i) { + compile_predicate(r.get_tail(i), pred); + body.push_back(pred); + } + for (unsigned i = utsz; i < sz; ++i) { + compile_expr(r.get_tail(i), tmp); + body.push_back(to_app(tmp)); + } + rule* r_new = rm.mk(head, body.size(), body.c_ptr(), 0, r.name(), false); + new_rules.add_rule(r_new); + IF_VERBOSE(20, r_new->display(m_ctx, verbose_stream());); + if (old_rules.is_output_predicate(r.get_decl())) { + new_rules.set_output_predicate(r_new->get_decl()); + } + return true; + } + + void compile_predicate(app* p, app_ref& result) { + sort_ref_vector domain(m); + func_decl* d = p->get_decl(); + SASSERT(d->get_family_id() == null_family_id); + for (unsigned i = 0; i < p->get_num_args(); ++i) { + domain.push_back(compile_sort(m.get_sort(p->get_arg(i)))); + } + func_decl_ref fn(m); + fn = m.mk_func_decl(d->get_name(), domain.size(), domain.c_ptr(), m.mk_bool_sort()); + m_ctx.register_predicate(fn, false); + expr_ref_vector args(m); + expr_ref tmp(m); + for (unsigned i = 0; i < p->get_num_args(); ++i) { + compile_expr(p->get_arg(i), tmp); + args.push_back(tmp); + } + result = m.mk_app(fn, args.size(), args.c_ptr()); + } + + void insert_cache(expr* e, expr* r) { + m_trail.push_back(r); + m_cache.insert(e, r); + } + + void compile_var(var* v, var_ref& result) { + expr* r; + if (m_cache.find(v, r)) { + result = to_var(r); + } + else { + unsigned idx = v->get_idx(); + result = m.mk_var(idx, compile_sort(v->get_sort())); + insert_cache(v, result); + } + } + + sort* compile_sort(sort* s) { + if (m.is_bool(s)) { + return s; + } + if (bv.is_bv_sort(s)) { + unsigned sz = bv.get_bv_size(s); + ddnf_mgr const& mgr = m_ddnfs.get(sz); + unsigned num_elems = mgr.size(); + unsigned nb = 1; + while ((1UL << nb) <= num_elems) { + ++nb; + } + return bv.mk_sort(nb); + } + UNREACHABLE(); + return 0; + } + + void compile_expr(expr* e, expr_ref& result) { + expr* r = 0; + if (m_cache.find(e, r)) { + result = r; + return; + } + + if (is_ground(e)) { + result = e; + m_cache.insert(e, result); + return; + } + + if (is_var(e)) { + var_ref w(m); + compile_var(to_var(e), w); + result = w; + return; + } + + if (m.is_and(e) || + m.is_or(e) || + m.is_iff(e) || + m.is_not(e) || + m.is_implies(e)) { + app* a = to_app(e); + expr_ref tmp(m); + expr_ref_vector args(m); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + compile_expr(a->get_arg(i), tmp); + args.push_back(tmp); + } + result = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); + insert_cache(e, result); + return; + } + + expr* e1, *e2, *e3; + unsigned lo, hi; + if (m.is_eq(e, e1, e2) && bv.is_bv(e1)) { + if (is_var(e1) && is_ground(e2)) { + compile_eq(e, result, to_var(e1), bv.get_bv_size(e1)-1, 0, e2); + } + else if (is_var(e2) && is_ground(e1)) { + compile_eq(e, result, to_var(e2), bv.get_bv_size(e2)-1, 0, e1); + } + else if (bv.is_extract(e1, lo, hi, e3) && is_var(e3) && is_ground(e2)) { + compile_eq(e, result, to_var(e3), hi, lo, e2); + } + else if (bv.is_extract(e2, lo, hi, e3) && is_var(e3) && is_ground(e1)) { + compile_eq(e, result, to_var(e3), hi, lo, e1); + } + else if (is_var(e1) && is_var(e2)) { + var_ref v1(m), v2(m); + compile_var(to_var(e1), v1); + compile_var(to_var(e2), v2); + result = m.mk_eq(v1, v2); + } + else { + UNREACHABLE(); + } + insert_cache(e, result); + return; + } + std::cout << mk_pp(e, m) << "\n"; + UNREACHABLE(); + } + + void compile_eq(expr* e, expr_ref& result, var* v, unsigned hi, unsigned lo, expr* c) { + tbv* t; + // TBD: hi, lo are ignored. + VERIFY(m_expr2tbv.find(e, t)); + var_ref w(m); + compile_var(v, w); + unsigned num_bits = bv.get_bv_size(c); + ddnf_nodes const& ns = m_ddnfs.lookup(num_bits, *t); + ddnf_nodes::iterator it = ns.begin(), end = ns.end(); + expr_ref_vector eqs(m); + sort* s = m.get_sort(w); + for (; it != end; ++it) { + eqs.push_back(m.mk_eq(w, bv.mk_numeral(rational((*it)->get_id()), s))); + } + switch (eqs.size()) { + case 0: + UNREACHABLE(); + result = m.mk_false(); + break; + case 1: + result = eqs[0].get(); + break; + default: + result = m.mk_or(eqs.size(), eqs.c_ptr()); + break; + } + } + }; + + ddnf::ddnf(context& ctx): + datalog::engine_base(ctx.get_manager(),"tabulation"), + m_imp(alloc(imp, ctx)) { + } + ddnf::~ddnf() { + dealloc(m_imp); + } + lbool ddnf::query(expr* query) { + return m_imp->query(query); + } + void ddnf::cancel() { + m_imp->cancel(); + } + void ddnf::cleanup() { + m_imp->cleanup(); + } + void ddnf::reset_statistics() { + m_imp->reset_statistics(); + } + void ddnf::collect_statistics(statistics& st) const { + m_imp->collect_statistics(st); + } + void ddnf::display_certificate(std::ostream& out) const { + m_imp->display_certificate(out); + } + expr_ref ddnf::get_answer() { + return m_imp->get_answer(); + } +}; diff --git a/src/muz/ddnf/ddnf.h b/src/muz/ddnf/ddnf.h new file mode 100644 index 000000000..7e1e83e52 --- /dev/null +++ b/src/muz/ddnf/ddnf.h @@ -0,0 +1,73 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + ddnf.h + +Abstract: + + DDNF based engine. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-08-21 + +Revision History: + +--*/ +#ifndef DDNF_H_ +#define DDNF_H_ + +#include "ast.h" +#include "lbool.h" +#include "statistics.h" +#include "dl_engine_base.h" + +class tbv; +class tbv_manager; + +namespace datalog { + class context; + + class ddnf : public engine_base { + class imp; + imp* m_imp; + public: + ddnf(context& ctx); + ~ddnf(); + virtual lbool query(expr* query); + virtual void cancel(); + virtual void cleanup(); + virtual void reset_statistics(); + virtual void collect_statistics(statistics& st) const; + virtual void display_certificate(std::ostream& out) const; + virtual expr_ref get_answer(); + }; + + class ddnf_node; + class ddnf_mgr; + class ddnf_core { + ddnf_mgr* m_imp; + public: + ddnf_core(unsigned n); + ~ddnf_core(); + ddnf_node* insert(tbv const& t); + tbv_manager& get_tbv_manager(); + unsigned size() const; + bool contains(tbv const& t); + bool well_formed(); + + // + // accumulate labels covered by the stream of tbvs, + // such that labels that are covered by the current + // tbv but not the previous tbvs are included. + // + void reset_accumulate(); + void accumulate(tbv const& t, unsigned_vector& labels); + void display(std::ostream& out) const; + void display_statistics(std::ostream& out) const; + }; +}; + +#endif diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index e49ddee81..4e173ca44 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -148,7 +148,7 @@ namespace Duality { // make a new problem and solver _d = alloc(duality_data,m_ctx.get_manager()); - _d->ctx.set("mbqi",m_ctx.get_params().mbqi()); + _d->ctx.set("mbqi",m_ctx.get_params().duality_mbqi()); _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); _d->rpfp = alloc(RPFP,_d->ls); @@ -185,7 +185,7 @@ namespace Duality { unsigned nrules = rs.get_num_rules(); for(unsigned i = 0; i < nrules; i++){ expr_ref f(m_ctx.get_manager()); - rs.get_rule(i)->to_formula(f); + rm.to_formula(*rs.get_rule(i), f); rules.push_back(f); } } @@ -283,7 +283,7 @@ namespace Duality { } } } - unsigned rb = m_ctx.get_params().recursion_bound(); + unsigned rb = m_ctx.get_params().duality_recursion_bound(); std::vector std_bounds; for(unsigned i = 0; i < bounds.size(); i++){ unsigned b = bounds[i]; @@ -307,14 +307,14 @@ namespace Duality { // set its options IF_VERBOSE(1, rs->SetOption("report","1");); - rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0"); - rs->SetOption("no_conj",m_ctx.get_params().no_conj() ? "1" : "0"); - rs->SetOption("feasible_edges",m_ctx.get_params().feasible_edges() ? "1" : "0"); - rs->SetOption("use_underapprox",m_ctx.get_params().use_underapprox() ? "1" : "0"); - rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0"); - rs->SetOption("batch_expand",m_ctx.get_params().batch_expand() ? "1" : "0"); - rs->SetOption("conjecture_file",m_ctx.get_params().conjecture_file()); - rs->SetOption("enable_restarts",m_ctx.get_params().enable_restarts() ? "1" : "0"); + rs->SetOption("full_expand",m_ctx.get_params().duality_full_expand() ? "1" : "0"); + rs->SetOption("no_conj",m_ctx.get_params().duality_no_conj() ? "1" : "0"); + rs->SetOption("feasible_edges",m_ctx.get_params().duality_feasible_edges() ? "1" : "0"); + rs->SetOption("use_underapprox",m_ctx.get_params().duality_use_underapprox() ? "1" : "0"); + rs->SetOption("stratified_inlining",m_ctx.get_params().duality_stratified_inlining() ? "1" : "0"); + rs->SetOption("batch_expand",m_ctx.get_params().duality_batch_expand() ? "1" : "0"); + rs->SetOption("conjecture_file",m_ctx.get_params().duality_conjecture_file()); + rs->SetOption("enable_restarts",m_ctx.get_params().duality_enable_restarts() ? "1" : "0"); #if 0 if(rb != UINT_MAX){ std::ostringstream os; os << rb; @@ -336,7 +336,7 @@ namespace Duality { // profile! - if(m_ctx.get_params().profile()) + if(m_ctx.get_params().duality_profile()) print_profile(std::cout); // save the result and counterexample if there is one diff --git a/src/muz/duality/duality_dl_interface.h b/src/muz/duality/duality_dl_interface.h index 0d739f5cc..a7c186074 100644 --- a/src/muz/duality/duality_dl_interface.h +++ b/src/muz/duality/duality_dl_interface.h @@ -18,8 +18,8 @@ --*/ -#ifndef _DUALITY_DL_INTERFACE_H_ -#define _DUALITY_DL_INTERFACE_H_ +#ifndef DUALITY_DL_INTERFACE_H_ +#define DUALITY_DL_INTERFACE_H_ #include "lbool.h" #include "dl_rule.h" diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 830f6d078..41c0f77af 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #include"datalog_parser.h" #include"string_buffer.h" #include"str_hashtable.h" @@ -41,15 +47,12 @@ class line_reader { static const char s_delimiter = '\n'; static const unsigned s_expansion_step = 1024; -#if 0 - std::istream & m_stm; -#else FILE * m_file; -#endif svector m_data; bool m_eof; bool m_eof_behind_buffer; unsigned m_next_index; + bool m_ok; //actually by one larger than the actual size of m_data, //to fit in the terminating delimiter @@ -88,35 +91,27 @@ class line_reader { public: -#if 0 - line_reader(std::istream & stm) - : m_stm(stm), - m_eof(false), - m_eof_behind_buffer(false), - m_next_index(0), - m_data_size(0) { - m_data.resize(2*s_expansion_step); - resize_data(0); - } -#else line_reader(const char * fname) - :m_eof(false), - m_eof_behind_buffer(false), - m_next_index(0), - m_data_size(0) { + :m_eof(false), + m_eof_behind_buffer(false), + m_next_index(0), + m_ok(true), + m_data_size(0) { m_data.resize(2*s_expansion_step); resize_data(0); #if _WINDOWS errno_t err = fopen_s(&m_file, fname, "rb"); - SASSERT(err==0); + m_ok = (m_file != NULL) && (err == 0); #else m_file = fopen(fname, "rb"); + m_ok = (m_file != NULL); #endif } ~line_reader() { fclose(m_file); } -#endif + + bool operator()() { return m_ok; } /** \brief Retrieve next line from the stream. @@ -170,6 +165,38 @@ public: bool eof() const { return m_eof; } }; +class char_reader { + line_reader m_line_reader; + char const* m_line; +public: + char_reader(char const* file): + m_line_reader(file), + m_line(0) + {} + + bool operator()() { return m_line_reader(); } + + char get() { + if (!m_line) { + if (m_line_reader.eof()) { + return EOF; + } + m_line = m_line_reader.get_line(); + } + if (!(m_line[0])) { + m_line = 0; + return '\n'; + } + char result = m_line[0]; + ++m_line; + return result; + } + + bool eof() { + return m_line_reader.eof(); + } +}; + class reserved_symbols { typedef map str2token; str2token m_str2token; @@ -200,6 +227,7 @@ public: class dlexer { std::istream* m_input; + char_reader* m_reader; char m_prev_char; char m_curr_char; int m_line; @@ -219,7 +247,12 @@ public: void next() { m_prev_char = m_curr_char; - m_curr_char = m_input->get(); + if (m_reader) { + m_curr_char = m_reader->get(); + } + else { + m_curr_char = m_input->get(); + } m_pos++; } @@ -234,6 +267,7 @@ public: dlexer(): m_input(0), + m_reader(0), m_prev_char(0), m_curr_char(0), m_line(1), @@ -242,8 +276,9 @@ public: m_parsing_domains(false) { } - void set_stream(std::istream& s) { - m_input = &s; + void set_stream(std::istream* s, char_reader* r) { + m_input = s; + m_reader = r; next(); } @@ -279,7 +314,10 @@ public: m_tok_pos = m_pos; next(); while (m_curr_char != '"') { - if (m_input->eof()) { + if (m_input && m_input->eof()) { + return TK_ERROR; + } + if (m_reader && m_reader->eof()) { return TK_ERROR; } if (m_curr_char == '\n') { @@ -431,7 +469,6 @@ protected: ast_manager& m_manager; dlexer* m_lexer; - ast_ref_vector m_pinned; region m_region; dl_decl_util & m_decl_util; arith_util m_arith; @@ -450,7 +487,6 @@ public: dparser(context& ctx, ast_manager& m): m_context(ctx), m_manager(m), - m_pinned(m), m_decl_util(ctx.get_decl_util()), m_arith(m), m_num_vars(0), @@ -460,17 +496,17 @@ public: virtual bool parse_file(char const * filename) { reset(); - if (filename != 0) { + if (filename != 0) { set_path(filename); - std::ifstream stream(filename); - if (!stream) { + char_reader reader(filename); + if (!reader()) { get_err() << "ERROR: could not open file '" << filename << "'.\n"; return false; } - return parse_stream(stream); + return parse_stream(0, &reader); } else { - return parse_stream(std::cin); + return parse_stream(&std::cin, 0); } } @@ -478,7 +514,7 @@ public: reset(); std::string s(string); std::istringstream is(s); - return parse_stream(is); + return parse_stream(&is, 0); } protected: @@ -486,7 +522,6 @@ protected: void reset() { m_num_vars = 0; m_sym_idx = 0; - m_pinned.reset(); m_vars.reset(); m_region.reset(); m_path.clear(); @@ -507,13 +542,13 @@ protected: return std::cerr; } - bool parse_stream(std::istream& is) { + bool parse_stream(std::istream* is, char_reader* r) { bool result = false; try { m_error=false; dlexer lexer; m_lexer = &lexer; - m_lexer->set_stream(is); + m_lexer->set_stream(is, r); dtoken tok = m_lexer->next_token(); tok = parse_domains(tok); tok = parse_decls(tok); @@ -630,7 +665,7 @@ protected: dtoken unexpected(dtoken tok, char const* msg) { #if 1 - throw default_exception("%s at line %u '%s' found '%s'\n", msg, + throw default_exception(default_exception::fmt(), "%s at line %u '%s' found '%s'\n", msg, m_lexer->get_line(), m_lexer->get_token_data(), dtoken_strings[tok]); SASSERT(false); @@ -741,7 +776,7 @@ protected: // dtoken parse_infix(dtoken tok1, char const* td, app_ref& pred) { symbol td1(td); - expr* v1 = 0, *v2 = 0; + expr_ref v1(m_manager), v2(m_manager); sort* s = 0; dtoken tok2 = m_lexer->next_token(); if (tok2 != TK_NEQ && tok2 != TK_GT && tok2 != TK_LT && tok2 != TK_EQ) { @@ -755,12 +790,16 @@ protected: symbol td2(td); if (tok1 == TK_ID) { - m_vars.find(td1.bare_str(), v1); + expr* _v1 = 0; + m_vars.find(td1.bare_str(), _v1); + v1 = _v1; } if (tok3 == TK_ID) { - m_vars.find(td2.bare_str(), v2); + expr* _v2 = 0; + m_vars.find(td2.bare_str(), _v2); + v2 = _v2; } - if (v1 == 0 && v2 == 0) { + if (!v1 && !v2) { return unexpected(tok3, "at least one argument should be a variable"); } if (v1) { @@ -919,7 +958,7 @@ protected: m_vars.insert(data.bare_str(), v); } else if (s != m_manager.get_sort(v)) { - throw default_exception("sort: %s expected, but got: %s\n", + throw default_exception(default_exception::fmt(), "sort: %s expected, but got: %s\n", s->get_name().bare_str(), m_manager.get_sort(v)->get_name().bare_str()); } args.push_back(v); @@ -963,10 +1002,11 @@ protected: } dtoken parse_include(char const* filename, bool parsing_domain) { + IF_VERBOSE(2, verbose_stream() << "include: " << filename << "\n";); std::string path(m_path); path += filename; - std::ifstream stream(path.c_str()); - if (!stream) { + char_reader stream(path.c_str()); + if (!stream()) { get_err() << "ERROR: could not open file '" << path << "'.\n"; return TK_ERROR; } @@ -974,7 +1014,7 @@ protected: dlexer lexer; { flet lexer_let(m_lexer, &lexer); - m_lexer->set_stream(stream); + m_lexer->set_stream(0, &stream); tok = m_lexer->next_token(); if(parsing_domain) { tok = parse_domains(tok); @@ -990,15 +1030,16 @@ protected: dtoken parse_mapfile(dtoken tok, sort * s, char const* filename) { std::string path(m_path); path += filename; - std::ifstream stream(path.c_str()); - if (!stream) { + line_reader reader(path.c_str()); + + if (!reader()) { get_err() << "Warning: could not open file '" << path << "'.\n"; return m_lexer->next_token(); } std::string line; - while(read_line(stream, line)) { - symbol sym=symbol(line.c_str()); + while(!reader.eof()) { + symbol sym=symbol(reader.get_line()); m_context.get_constant_number(s, sym); } return m_lexer->next_token(); @@ -1018,10 +1059,6 @@ protected: } void add_rule(app* head, unsigned sz, app* const* body, const bool * is_neg) { - m_pinned.push_back(head); - for (unsigned i = 0; i < sz; ++i) { - m_pinned.push_back(body[i]); - } rule_manager& m = m_context.get_rule_manager(); if(sz==0 && m.is_fact(head)) { @@ -1037,7 +1074,7 @@ protected: sort * register_finite_sort(symbol name, uint64 domain_size, context::sort_kind k) { if(m_sort_dict.contains(name.bare_str())) { - throw default_exception("sort %s already declared", name.bare_str()); + throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str()); } sort * s = m_decl_util.mk_sort(name, domain_size); m_context.register_finite_sort(s, k); @@ -1047,7 +1084,7 @@ protected: sort * register_int_sort(symbol name) { if(m_sort_dict.contains(name.bare_str())) { - throw default_exception("sort %s already declared", name.bare_str()); + throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str()); } sort * s = m_arith.mk_int(); m_sort_dict.insert(name.bare_str(), s); @@ -1057,7 +1094,7 @@ protected: sort * get_sort(const char* str) { sort * res; if(!m_sort_dict.find(str, res)) { - throw default_exception("unknown sort \"%s\"", str); + throw default_exception(default_exception::fmt(), "unknown sort \"%s\"", str); } return res; } @@ -1067,7 +1104,7 @@ protected: if(m_arith.is_int(s)) { uint64 val; if(!string_to_uint64(name.bare_str(), val)) { - throw default_exception("Invalid integer: \"&s\"", name.bare_str()); + throw default_exception(default_exception::fmt(), "Invalid integer: \"%s\"", name.bare_str()); } res = m_arith.mk_numeral(rational(val, rational::ui64()), s); } @@ -1075,7 +1112,6 @@ protected: unsigned idx = m_context.get_constant_number(s, name); res = m_decl_util.mk_numeral(idx, s); } - m_pinned.push_back(res); return res; } /** @@ -1090,13 +1126,11 @@ protected: unsigned idx = m_context.get_constant_number(s, symbol(to_string(el).c_str())); res = m_decl_util.mk_numeral(idx, s); } - m_pinned.push_back(res); return res; } app* mk_const(uint64 el, sort* s) { unsigned idx = m_context.get_constant_number(s, el); app * res = m_decl_util.mk_numeral(idx, s); - m_pinned.push_back(res); return res; } @@ -1261,7 +1295,7 @@ private: if(num==0) { const_name = symbol(""); } else if(!m_number_names.find(num, const_name)) { - throw default_exception("unknown symbol number %I64u on line %d in file %s", + throw default_exception(default_exception::fmt(), "unknown symbol number %I64u on line %d in file %s", num, m_current_line, m_current_file.c_str()); } res = mk_table_const(const_name, s); @@ -1278,7 +1312,7 @@ private: dlexer lexer; m_lexer = &lexer; - m_lexer->set_stream(stm); + m_lexer->set_stream(&stm, 0); dtoken tok = m_lexer->next_token(); tok = parse_decls(tok); m_lexer = 0; @@ -1300,12 +1334,13 @@ private: } uint64 num; if(!read_uint64(ptr, num)) { - throw default_exception("number expected on line %d in file %s", + throw default_exception(default_exception::fmt(), "number expected on line %d in file %s", m_current_line, m_current_file.c_str()); } if(*ptr!=' ' && *ptr!=0) { - throw default_exception("' ' expected to separate numbers on line %d in file %s, got '%s'", - m_current_line, m_current_file.c_str(), ptr); + throw default_exception(default_exception::fmt(), + "' ' expected to separate numbers on line %d in file %s, got '%s'", + m_current_line, m_current_file.c_str(), ptr); } args.push_back(num); } while(!last); @@ -1325,7 +1360,7 @@ private: func_decl * pred = m_context.try_get_predicate_decl(predicate_name); if(!pred) { - throw default_exception("tuple file %s for undeclared predicate %s", + throw default_exception(default_exception::fmt(), "tuple file %s for undeclared predicate %s", m_current_file.c_str(), predicate_name.bare_str()); } unsigned pred_arity = pred->get_arity(); @@ -1347,7 +1382,7 @@ private: continue; } if(args.size()!=pred_arity) { - throw default_exception("invalid number of arguments on line %d in file %s", + throw default_exception(default_exception::fmt(), "invalid number of arguments on line %d in file %s", m_current_line, m_current_file.c_str()); } @@ -1416,10 +1451,12 @@ private: const char * ptr = full_line; if(!read_uint64(ptr, num)) { - throw default_exception("number expected at line %d in file %s", m_current_line, m_current_file.c_str()); + throw default_exception(default_exception::fmt(), + "number expected at line %d in file %s", m_current_line, m_current_file.c_str()); } if(*ptr!=' ') { - throw default_exception("' ' expected after the number at line %d in file %s", m_current_line, m_current_file.c_str()); + throw default_exception(default_exception::fmt(), + "' ' expected after the number at line %d in file %s", m_current_line, m_current_file.c_str()); } ptr++; diff --git a/src/muz/fp/datalog_parser.h b/src/muz/fp/datalog_parser.h index 88ba3ab72..70b251585 100644 --- a/src/muz/fp/datalog_parser.h +++ b/src/muz/fp/datalog_parser.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DATALOG_PARSER_H_ -#define _DATALOG_PARSER_H_ +#ifndef DATALOG_PARSER_H_ +#define DATALOG_PARSER_H_ #include "ast.h" #include "dl_context.h" diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index a0af94953..a583d7a26 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -103,7 +103,7 @@ struct dl_context { void add_rule(expr * rule, symbol const& name, unsigned bound) { init(); if (m_collected_cmds) { - expr_ref rl = m_context->bind_variables(rule, true); + expr_ref rl = m_context->bind_vars(rule, true); m_collected_cmds->m_rules.push_back(rl); m_collected_cmds->m_names.push_back(name); m_trail.push(push_back_vector(m_collected_cmds->m_rules)); @@ -116,7 +116,7 @@ struct dl_context { bool collect_query(expr* q) { if (m_collected_cmds) { - expr_ref qr = m_context->bind_variables(q, false); + expr_ref qr = m_context->bind_vars(q, false); m_collected_cmds->m_queries.push_back(qr); m_trail.push(push_back_vector(m_collected_cmds->m_queries)); return true; @@ -234,6 +234,7 @@ public: bool query_exn = false; lbool status = l_undef; { + IF_VERBOSE(10, verbose_stream() << "(query)\n";); scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); @@ -337,12 +338,8 @@ private: if (m_dl_ctx->get_params().print_statistics()) { statistics st; datalog::context& dlctx = m_dl_ctx->dlctx(); - unsigned long long max_mem = memory::get_max_used_memory(); - unsigned long long mem = memory::get_allocation_size(); dlctx.collect_statistics(st); st.update("time", ctx.get_seconds()); - st.update("memory", static_cast(mem)/static_cast(1024*1024)); - st.update("max-memory", static_cast(max_mem)/static_cast(1024*1024)); st.display_smt2(ctx.regular_stream()); } } diff --git a/src/muz/fp/dl_cmds.h b/src/muz/fp/dl_cmds.h index d71b319c4..a06e6140e 100644 --- a/src/muz/fp/dl_cmds.h +++ b/src/muz/fp/dl_cmds.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _DL_CMDS_H_ -#define _DL_CMDS_H_ +#ifndef DL_CMDS_H_ +#define DL_CMDS_H_ #include "ast.h" diff --git a/src/muz/fp/dl_register_engine.cpp b/src/muz/fp/dl_register_engine.cpp index 57413b7cb..6e1ba40ce 100644 --- a/src/muz/fp/dl_register_engine.cpp +++ b/src/muz/fp/dl_register_engine.cpp @@ -22,6 +22,7 @@ Revision History: #include "tab_context.h" #include "rel_context.h" #include "pdr_dl_interface.h" +#include "ddnf.h" #include "duality_dl_interface.h" namespace datalog { @@ -43,6 +44,8 @@ namespace datalog { return alloc(clp, *m_ctx); case DUALITY_ENGINE: return alloc(Duality::dl_interface, *m_ctx); + case DDNF_ENGINE: + return alloc(ddnf, *m_ctx); case LAST_ENGINE: UNREACHABLE(); return 0; diff --git a/src/muz/fp/dl_register_engine.h b/src/muz/fp/dl_register_engine.h index 44f5090e6..4d5076b75 100644 --- a/src/muz/fp/dl_register_engine.h +++ b/src/muz/fp/dl_register_engine.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_REGISTER_ENGINE_H_ -#define _DL_REGISTER_ENGINE_H_ +#ifndef DL_REGISTER_ENGINE_H_ +#define DL_REGISTER_ENGINE_H_ #include "dl_context.h" diff --git a/src/muz/fp/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index 0c094c277..8e9e48f55 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -28,6 +28,7 @@ Revision History: #include"filter_model_converter.h" #include"dl_transforms.h" #include"fixedpoint_params.hpp" +#include"ast_util.h" class horn_tactic : public tactic { struct imp { @@ -145,7 +146,7 @@ class horn_tactic : public tactic { expr_ref_vector args(m), body(m); expr_ref head(m); expr* a = 0, *a1 = 0; - qe::flatten_or(tmp, args); + flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { a = args[i].get(); check_predicate(mark, a); @@ -190,7 +191,7 @@ class horn_tactic : public tactic { bool produce_proofs = g->proofs_enabled(); if (produce_proofs) { - if (!m_ctx.get_params().generate_proof_trace()) { + if (!m_ctx.generate_proof_trace()) { params_ref params = m_ctx.get_params().p; params.set_bool("generate_proof_trace", true); updt_params(params); @@ -316,7 +317,7 @@ class horn_tactic : public tactic { m_ctx.get_rules(); // flush adding rules. apply_default_transformation(m_ctx); - if (m_ctx.get_params().slice()) { + if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); @@ -334,7 +335,7 @@ class horn_tactic : public tactic { datalog::rule_set::iterator it = rules.begin(), end = rules.end(); for (; it != end; ++it) { datalog::rule* r = *it; - r->to_formula(fml); + m_ctx.get_rule_manager().to_formula(*r, fml); (*rep)(fml); g->assert_expr(fml); } diff --git a/src/muz/fp/horn_tactic.h b/src/muz/fp/horn_tactic.h index f041321ea..72432b4e2 100644 --- a/src/muz/fp/horn_tactic.h +++ b/src/muz/fp/horn_tactic.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _HORN_TACTIC_H_ -#define _HORN_TACTIC_H_ +#ifndef HORN_TACTIC_H_ +#define HORN_TACTIC_H_ #include"params.h" class ast_manager; diff --git a/src/muz/pdr/pdr_closure.cpp b/src/muz/pdr/pdr_closure.cpp index 86af8b2f9..eacbb0b28 100644 --- a/src/muz/pdr/pdr_closure.cpp +++ b/src/muz/pdr/pdr_closure.cpp @@ -143,7 +143,7 @@ namespace pdr { expr_ref closure::close_conjunction(expr* fml) { expr_ref_vector fmls(m); - qe::flatten_and(fml, fmls); + flatten_and(fml, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { fmls[i] = close_fml(fmls[i].get()); } diff --git a/src/muz/pdr/pdr_closure.h b/src/muz/pdr/pdr_closure.h index 885dbce8d..ab417e90e 100644 --- a/src/muz/pdr/pdr_closure.h +++ b/src/muz/pdr/pdr_closure.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _PDR_CLOSURE_H_ -#define _PDR_CLOSURE_H_ +#ifndef PDR_CLOSURE_H_ +#define PDR_CLOSURE_H_ #include "arith_decl_plugin.h" diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index a1f3af401..d349da4ec 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -47,6 +47,8 @@ Notes: #include "dl_boogie_proof.h" #include "qe_util.h" #include "scoped_proof.h" +#include "blast_term_ite_tactic.h" +#include "model_implicant.h" #include "expr_safe_replace.h" namespace pdr { @@ -78,9 +80,9 @@ namespace pdr { pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): pm(pm), m(pm.get_manager()), ctx(ctx), m_head(head, m), - m_sig(m), m_solver(pm, ctx.get_params(), head->get_name()), + m_sig(m), m_solver(pm, head->get_name()), m_invariants(m), m_transition(m), m_initial_state(m), - m_reachable(pm, (datalog::PDR_CACHE_MODE)ctx.get_params().cache_mode()) {} + m_reachable(pm, (datalog::PDR_CACHE_MODE)ctx.get_params().pdr_cache_mode()) {} pred_transformer::~pred_transformer() { rule2inst::iterator it2 = m_rule2inst.begin(), end2 = m_rule2inst.end(); @@ -95,8 +97,9 @@ namespace pdr { std::ostream& pred_transformer::display(std::ostream& out) const { if (!rules().empty()) out << "rules\n"; + datalog::rule_manager& rm = ctx.get_context().get_rule_manager(); for (unsigned i = 0; i < rules().size(); ++i) { - rules()[i]->display_smt2(m, out) << "\n"; + rm.display_smt2(*rules()[i], out) << "\n"; } out << "transition\n" << mk_pp(transition(), m) << "\n"; return out; @@ -149,10 +152,11 @@ namespace pdr { datalog::rule const& pred_transformer::find_rule(model_core const& model) const { obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); TRACE("pdr_verbose", + datalog::rule_manager& rm = ctx.get_context().get_rule_manager(); for (; it != end; ++it) { expr* pred = it->m_key; tout << mk_pp(pred, m) << ":\n"; - if (it->m_value) it->m_value->display_smt2(m, tout) << "\n"; + if (it->m_value) rm.display_smt2(*it->m_value, tout) << "\n"; } ); @@ -345,7 +349,7 @@ namespace pdr { void pred_transformer::add_property(expr* lemma, unsigned lvl) { expr_ref_vector lemmas(m); - qe::flatten_and(lemma, lemmas); + flatten_and(lemma, lemmas); for (unsigned i = 0; i < lemmas.size(); ++i) { expr* lemma_i = lemmas[i].get(); if (add_property1(lemma_i, lvl)) { @@ -590,7 +594,7 @@ namespace pdr { for (unsigned i = ut_size; i < t_size; ++i) { tail.push_back(rule.get_tail(i)); } - qe::flatten_and(tail); + flatten_and(tail); for (unsigned i = 0; i < tail.size(); ++i) { expr_ref tmp(m); var_subst(m, false)(tail[i].get(), var_reprs.size(), (expr*const*)var_reprs.c_ptr(), tmp); @@ -602,7 +606,7 @@ namespace pdr { th_rewriter rw(m); rw(fml); if (ctx.is_dl() || ctx.is_utvpi()) { - hoist_non_bool_if(fml); + blast_term_ite(fml); } TRACE("pdr", tout << mk_pp(fml, m) << "\n";); SASSERT(is_ground(fml)); @@ -618,14 +622,14 @@ namespace pdr { m_rule2transition.insert(&rule, fml.get()); rules.push_back(&rule); } - m_rule2inst.insert(&rule,&var_reprs); + m_rule2inst.insert(&rule, &var_reprs); m_rule2vars.insert(&rule, aux_vars); TRACE("pdr", tout << rule.get_decl()->get_name() << "\n"; for (unsigned i = 0; i < var_reprs.size(); ++i) { tout << mk_pp(var_reprs[i].get(), m) << " "; } - tout << "\n";); + if (!var_reprs.empty()) tout << "\n";); } bool pred_transformer::check_filled(app_ref_vector const& v) const { @@ -637,14 +641,14 @@ namespace pdr { // create constants for free variables in tail. void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars) { - ptr_vector sorts; - get_free_vars(e, sorts); - while (vars.size() < sorts.size()) { + expr_free_vars fv; + fv(e); + while (vars.size() < fv.size()) { vars.push_back(0); } - for (unsigned i = 0; i < sorts.size(); ++i) { - if (sorts[i] && !vars[i].get()) { - vars[i] = m.mk_fresh_const("aux", sorts[i]); + for (unsigned i = 0; i < fv.size(); ++i) { + if (fv[i] && !vars[i].get()) { + vars[i] = m.mk_fresh_const("aux", fv[i]); aux_vars.push_back(vars[i].get()); } } @@ -725,6 +729,7 @@ namespace pdr { obj_map::iterator it = other.m_prop2level.begin(); obj_map::iterator end = other.m_prop2level.end(); for (; it != end; ++it) { + IF_VERBOSE(2, verbose_stream() << "(pdr-inherit: " << mk_pp(it->m_key, m) << ")\n";); add_property(it->m_key, it->m_value); } } @@ -737,9 +742,26 @@ namespace pdr { m_closed = true; } - void model_node::reopen() { + void model_node::set_open() { SASSERT(m_closed); m_closed = false; + model_node* p = parent(); + while (p && p->is_closed()) { + p->m_closed = false; + p = p->parent(); + } + } + + void model_node::check_pre_closed() { + for (unsigned i = 0; i < children().size(); ++i) { + if (children()[i]->is_open()) return; + } + set_pre_closed(); + model_node* p = parent(); + while (p && p->is_1closed()) { + p->set_pre_closed(); + p = p->parent(); + } } static bool is_ini(datalog::rule const& r) { @@ -787,7 +809,7 @@ namespace pdr { ast_manager& m = pt().get_manager(); expr_ref_vector conjs(m); obj_map model; - qe::flatten_and(state(), conjs); + flatten_and(state(), conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(), *e1, *e2; if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { @@ -847,22 +869,66 @@ namespace pdr { } + void model_node::dequeue(model_node*& root) { + TRACE("pdr", tout << this << " " << state() << "\n";); + if (!m_next && !m_prev) return; + SASSERT(m_next); + SASSERT(m_prev); + SASSERT(children().empty()); + if (this == m_next) { + SASSERT(root == this); + root = 0; + } + else { + m_next->m_prev = m_prev; + m_prev->m_next = m_next; + if (this == root) { + root = m_next; + } + } + TRACE("pdr", tout << "new root: " << root << "\n";); + m_prev = 0; + m_next = 0; + } + + + void model_node::enqueue(model_node* n) { + TRACE("pdr", tout << n << " " << n->state() << "\n";); + SASSERT(!n->m_next); + SASSERT(!n->m_prev); + if (this == n) { + m_next = n; + m_prev = n; + } + else { + n->m_next = m_next; + m_next->m_prev = n; + m_next = n; + n->m_prev = this; + } + } // ---------------- // model_search + /** + \brief Dequeue the next goal. + */ model_node* model_search::next() { - if (m_leaves.empty()) { + if (!m_goal) { return 0; } - model_node* result = m_leaves.back(); - m_leaves.pop_back(); - return result; + else { + model_node* result = m_goal; + result->dequeue(m_goal); + return result; + } } bool model_search::is_repeated(model_node& n) const { model_node* p = n.parent(); while (p) { if (p->state() == n.state()) { + TRACE("pdr", tout << "repeated\n";); return true; } p = p->parent(); @@ -871,10 +937,15 @@ namespace pdr { } void model_search::add_leaf(model_node& n) { + SASSERT(n.children().empty()); model_nodes ns; model_nodes& nodes = cache(n).insert_if_not_there2(n.state(), ns)->get_data().m_value; + if (nodes.contains(&n)) { + return; + } nodes.push_back(&n); - if (nodes.size() == 1 || is_repeated(n)) { + TRACE("pdr_verbose", tout << "add: " << n.level() << ": " << &n << " " << n.state() << "\n";); + if (nodes.size() == 1) { set_leaf(n); } else { @@ -885,15 +956,21 @@ namespace pdr { void model_search::set_leaf(model_node& n) { erase_children(n, true); SASSERT(n.is_open()); - enqueue_leaf(n); + enqueue_leaf(&n); } - void model_search::enqueue_leaf(model_node& n) { - if (m_bfs) { - m_leaves.push_front(&n); + void model_search::enqueue_leaf(model_node* n) { + TRACE("pdr_verbose", tout << n << " " << n->state() << " goal: " << m_goal << "\n";); + SASSERT(n->is_open()); + if (!m_goal) { + m_goal = n; + m_goal->enqueue(n); + } + else if (m_bfs) { + m_goal->enqueue(n); } else { - m_leaves.push_back(&n); + m_goal->next()->enqueue(n); } } @@ -916,56 +993,132 @@ namespace pdr { void model_search::erase_children(model_node& n, bool backtrack) { ptr_vector todo, nodes; todo.append(n.children()); - erase_leaf(n); + remove_goal(n); n.reset(); while (!todo.empty()) { model_node* m = todo.back(); todo.pop_back(); nodes.push_back(m); todo.append(m->children()); - erase_leaf(*m); remove_node(*m, backtrack); } std::for_each(nodes.begin(), nodes.end(), delete_proc()); } void model_search::remove_node(model_node& n, bool backtrack) { + TRACE("pdr_verbose", tout << "remove: " << n.level() << ": " << &n << " " << n.state() << "\n";); model_nodes& nodes = cache(n).find(n.state()); nodes.erase(&n); - if (nodes.size() > 0 && n.is_open() && backtrack) { - for (unsigned i = 0; i < nodes.size(); ++i) { - nodes[i]->reopen(); - } + bool is_goal = n.is_goal(); + remove_goal(n); + if (!nodes.empty() && is_goal && backtrack) { + TRACE("pdr_verbose", for (unsigned i = 0; i < nodes.size(); ++i) n.display(tout << &n << "\n", 2);); + model_node* n1 = nodes[0]; + n1->set_open(); + SASSERT(n1->children().empty()); + enqueue_leaf(n1); } if (nodes.empty()) { cache(n).remove(n.state()); } } - void model_search::erase_leaf(model_node& n) { - if (n.children().empty() && n.is_open()) { - std::deque::iterator - it = m_leaves.begin(), - end = m_leaves.end(); - for (; it != end; ++it) { - if (*it == &n) { - m_leaves.erase(it); - break; + void model_search::remove_goal(model_node& n) { + n.dequeue(m_goal); + } + + void model_search::well_formed() { + // each open leaf is in the set of m_goal. + ptr_vector nodes; + nodes.push_back(&get_root()); + for (unsigned i = 0; i < nodes.size(); ++i) { + model_node* n = nodes[i]; + if (!n->children().empty()) { + nodes.append(n->children()); + } + else if (n->is_open() && !n->is_goal() && n->parent()) { + TRACE("pdr", n->display(tout << "node " << n << " not found among leaves\n", 0); display(tout);); + UNREACHABLE(); + return; + } + } + if (m_goal) { + model_node* n = m_goal; + do { + if (!n->is_open() || !n->children().empty()) { + TRACE("pdr", n->display(tout << "invalid leaf\n", 0); + display(tout);); + UNREACHABLE(); + return; + } + n = n->next(); + } + while (m_goal != n); + } + + // each state appears in at most one goal per level. + bool found = true; + for (unsigned l = 0; m_goal && found; ++l) { + found = false; + obj_hashtable open_states; + model_node* n = m_goal; + do { + if (n->level() == l) { + found = true; + if (n->is_open()) { + if (open_states.contains(n->state())) { + TRACE("pdr", n->display(tout << "repeated leaf\n", 0); display(tout);); + UNREACHABLE(); + } + open_states.insert(n->state()); + } + } + n = n->next(); + } + while (m_goal != n); + } + // a node is open if and only if it contains an + // open child which is a goal. + for (unsigned i = 0; i < nodes.size(); ++i) { + model_node* n = nodes[i]; + if (!n->children().empty() && n->parent()) { + found = false; + for (unsigned j = 0; !found && j < n->children().size(); ++j) { + found = n->children()[j]->is_open(); + } + if (n->is_open() != found) { + TRACE("pdr", n->display(tout << "node in inconsistent state\n", 0); display(tout);); + UNREACHABLE(); } } } } + unsigned model_search::num_goals() const { + model_node* n = m_goal; + unsigned num = 0; + if (n) { + do { + ++num; + n = n->next(); + } + while (n != m_goal); + } + return num; + } + std::ostream& model_search::display(std::ostream& out) const { if (m_root) { m_root->display(out, 0); } - out << "goals\n"; - std::deque::const_iterator - it = m_leaves.begin(), - end = m_leaves.end(); - for (; it != end; ++it) { - (*it)->display(out, 1); + out << "goals " << num_goals() << "\n"; + model_node* n = m_goal; + if (n) { + do { + n->display(out, 1); + n = n->next(); + } + while (n != m_goal); } return out; } @@ -994,12 +1147,18 @@ namespace pdr { model_node* n = todo.back(); model* md = 0; ast_manager& m = n->pt().get_manager(); - if (!n->get_model_ptr() && models.find(n->state(), md)) { - TRACE("pdr", tout << mk_pp(n->state(), m) << "\n";); - model_ref mr(md); - n->set_model(mr); - datalog::rule const* rule = rules.find(n->state()); - n->set_rule(rule); + if (!n->get_model_ptr()) { + if (models.find(n->state(), md)) { + TRACE("pdr", tout << mk_pp(n->state(), m) << "\n";); + model_ref mr(md); + n->set_model(mr); + datalog::rule const* rule = rules.find(n->state()); + n->set_rule(rule); + } + else { + IF_VERBOSE(1, n->display(verbose_stream() << "no model:\n", 0); + verbose_stream() << mk_pp(n->state(), m) << "\n";); + } } todo.pop_back(); todo.append(n->children().size(), n->children().c_ptr()); @@ -1086,7 +1245,6 @@ namespace pdr { children.append(n->children()); } - expr_safe_replace repl(m); for (unsigned i = 0; i < constraints.size(); ++i) { expr* e = constraints[i].get(), *e1, *e2; @@ -1216,7 +1374,7 @@ namespace pdr { } expr_ref fml_concl(m); - reduced_rule->to_formula(fml_concl); + rm.to_formula(*reduced_rule.get(), fml_concl); p1 = m.mk_hyper_resolve(pfs.size(), pfs.c_ptr(), fml_concl, positions, substs); } @@ -1240,8 +1398,8 @@ namespace pdr { remove_node(*m_root, false); dealloc(m_root); m_root = 0; - m_cache.reset(); } + m_cache.reset(); } void model_search::backtrack_level(bool uses_level, model_node& n) { @@ -1249,7 +1407,7 @@ namespace pdr { if (uses_level && m_root->level() > n.level()) { IF_VERBOSE(2, verbose_stream() << "Increase level " << n.level() << "\n";); n.increase_level(); - enqueue_leaf(n); + enqueue_leaf(&n); } else { model_node* p = n.parent(); @@ -1257,24 +1415,25 @@ namespace pdr { set_leaf(*p); } } + DEBUG_CODE(well_formed();); } // ---------------- // context context::context( - smt_params& fparams, - fixedpoint_params const& params, - ast_manager& m + smt_params& fparams, + fixedpoint_params const& params, + ast_manager& m ) : m_fparams(fparams), m_params(params), m(m), m_context(0), - m_pm(m_fparams, params.max_num_contexts(), m), + m_pm(m_fparams, params.pdr_max_num_contexts(), m), m_query_pred(m), m_query(0), - m_search(m_params.bfs_model_search()), + m_search(m_params.pdr_bfs_model_search()), m_last_result(l_undef), m_inductive_lvl(0), m_expanded_lvl(0), @@ -1502,93 +1661,106 @@ namespace pdr { } } }; - - void context::validate() { - if (!m_params.validate_result()) { - return; - } + + void context::validate_proof() { std::stringstream msg; - - switch(m_last_result) { - case l_true: { - proof_ref pr = get_proof(); - proof_checker checker(m); - expr_ref_vector side_conditions(m); - bool ok = checker.check(pr, side_conditions); - if (!ok) { - msg << "proof validation failed"; + proof_ref pr = get_proof(); + proof_checker checker(m); + expr_ref_vector side_conditions(m); + bool ok = checker.check(pr, side_conditions); + if (!ok) { + msg << "proof validation failed"; + IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); + throw default_exception(msg.str()); + } + for (unsigned i = 0; i < side_conditions.size(); ++i) { + expr* cond = side_conditions[i].get(); + expr_ref tmp(m); + + tmp = m.mk_not(cond); + IF_VERBOSE(2, verbose_stream() << "checking side-condition:\n" << mk_pp(cond, m) << "\n";); + smt::kernel solver(m, get_fparams()); + solver.assert_expr(tmp); + lbool res = solver.check(); + if (res != l_false) { + msg << "rule validation failed when checking: " << mk_pp(cond, m); IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); - } - for (unsigned i = 0; i < side_conditions.size(); ++i) { - expr* cond = side_conditions[i].get(); - expr_ref tmp(m); - tmp = m.mk_not(cond); - IF_VERBOSE(2, verbose_stream() << "checking side-condition:\n" << mk_pp(cond, m) << "\n";); + } + } + } + + void context::validate_search() { + expr_ref tr = m_search.get_trace(*this); + // TBD: tr << "\n"; + } + + void context::validate_model() { + std::stringstream msg; + expr_ref_vector refs(m); + expr_ref tmp(m); + model_ref model; + vector rs; + model_converter_ref mc; + get_level_property(m_inductive_lvl, refs, rs); + inductive_property ex(m, mc, rs); + ex.to_model(model); + decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); + var_subst vs(m, false); + expr_free_vars fv; + for (; it != end; ++it) { + ptr_vector const& rules = it->m_value->rules(); + for (unsigned i = 0; i < rules.size(); ++i) { + datalog::rule& r = *rules[i]; + model->eval(r.get_head(), tmp); + expr_ref_vector fmls(m); + fmls.push_back(m.mk_not(tmp)); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < utsz; ++j) { + model->eval(r.get_tail(j), tmp); + fmls.push_back(tmp); + } + for (unsigned j = utsz; j < tsz; ++j) { + fmls.push_back(r.get_tail(j)); + } + tmp = m.mk_and(fmls.size(), fmls.c_ptr()); + svector names; + fv(tmp); + fv.set_default_sort(m.mk_bool_sort()); + for (unsigned i = 0; i < fv.size(); ++i) { + names.push_back(symbol(i)); + } + fv.reverse(); + if (!fv.empty()) { + tmp = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), tmp); + } smt::kernel solver(m, get_fparams()); solver.assert_expr(tmp); lbool res = solver.check(); if (res != l_false) { - msg << "rule validation failed when checking: " << mk_pp(cond, m); + msg << "rule validation failed when checking: " << mk_pp(tmp, m); IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); throw default_exception(msg.str()); - } - } - break; - } - case l_false: { - expr_ref_vector refs(m); - expr_ref tmp(m); - model_ref model; - vector rs; - model_converter_ref mc; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, mc, rs); - ex.to_model(model); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - var_subst vs(m, false); - for (; it != end; ++it) { - ptr_vector const& rules = it->m_value->rules(); - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule& r = *rules[i]; - model->eval(r.get_head(), tmp); - expr_ref_vector fmls(m); - fmls.push_back(m.mk_not(tmp)); - unsigned utsz = r.get_uninterpreted_tail_size(); - unsigned tsz = r.get_tail_size(); - for (unsigned j = 0; j < utsz; ++j) { - model->eval(r.get_tail(j), tmp); - fmls.push_back(tmp); - } - for (unsigned j = utsz; j < tsz; ++j) { - fmls.push_back(r.get_tail(j)); - } - tmp = m.mk_and(fmls.size(), fmls.c_ptr()); - ptr_vector sorts; - svector names; - get_free_vars(tmp, sorts); - for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } - names.push_back(symbol(i)); - } - sorts.reverse(); - if (!sorts.empty()) { - tmp = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp); - } - smt::kernel solver(m, get_fparams()); - solver.assert_expr(tmp); - lbool res = solver.check(); - if (res != l_false) { - msg << "rule validation failed when checking: " << mk_pp(tmp, m); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } } } - break; } + } + + void context::validate() { + if (!m_params.pdr_validate_result()) { + return; + } + switch(m_last_result) { + case l_true: + if (m_params.generate_proof_trace()) { + validate_proof(); + } + validate_search(); + break; + case l_false: + validate_model(); + break; default: break; } @@ -1602,7 +1774,7 @@ namespace pdr { void context::init_core_generalizers(datalog::rule_set& rules) { reset_core_generalizers(); classifier_proc classify(m, rules); - bool use_mc = m_params.use_multicore_generalizer(); + bool use_mc = m_params.pdr_use_multicore_generalizer(); if (use_mc) { m_core_generalizers.push_back(alloc(core_multi_generalizer, *this, 0)); } @@ -1612,9 +1784,9 @@ namespace pdr { m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; m_fparams.m_arith_eager_eq_axioms = false; - if (m_params.use_utvpi() && - !m_params.use_convex_closure_generalizer() && - !m_params.use_convex_interior_generalizer()) { + if (m_params.pdr_utvpi() && + !m_params.pdr_use_convex_closure_generalizer() && + !m_params.pdr_use_convex_interior_generalizer()) { if (classify.is_dl()) { m_fparams.m_arith_mode = AS_DIFF_LOGIC; m_fparams.m_arith_expand_eqs = true; @@ -1626,19 +1798,19 @@ namespace pdr { } } } - if (m_params.use_convex_closure_generalizer()) { + if (m_params.pdr_use_convex_closure_generalizer()) { m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, true)); } - if (m_params.use_convex_interior_generalizer()) { + if (m_params.pdr_use_convex_interior_generalizer()) { m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, false)); } - if (!use_mc && m_params.use_inductive_generalizer()) { + if (!use_mc && m_params.pdr_use_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); } - if (m_params.inductive_reachability_check()) { + if (m_params.pdr_inductive_reachability_check()) { m_core_generalizers.push_back(alloc(core_induction_generalizer, *this)); } - if (m_params.use_arith_inductive_generalizer()) { + if (m_params.pdr_use_arith_inductive_generalizer()) { m_core_generalizers.push_back(alloc(core_arith_inductive_generalizer, *this)); } @@ -1702,7 +1874,7 @@ namespace pdr { decl2rel::iterator it = m_rels.begin (), end = m_rels.end (); for (; m_inductive_lvl > 0 && it != end; ++it) { if (it->m_value->head() != m_query_pred) { - it->m_value->propagate_to_infinity (m_inductive_lvl); + it->m_value->propagate_to_infinity (m_inductive_lvl); } } validate(); @@ -1836,17 +2008,6 @@ namespace pdr { } } - void context::check_pre_closed(model_node& n) { - for (unsigned i = 0; i < n.children().size(); ++i) { - if (!n.children()[i]->is_closed()) return; - } - n.set_pre_closed(); - model_node* p = n.parent(); - while (p && p->is_1closed()) { - p->set_pre_closed(); - p = p->parent(); - } - } void context::expand_node(model_node& n) { SASSERT(n.is_open()); @@ -1856,7 +2017,7 @@ namespace pdr { m_expanded_lvl = n.level(); } - pred_transformer::scoped_farkas sf (n.pt(), m_params.use_farkas()); + pred_transformer::scoped_farkas sf (n.pt(), m_params.pdr_farkas()); if (n.pt().is_reachable(n.state())) { TRACE("pdr", tout << "reachable\n";); close_node(n); @@ -1897,7 +2058,7 @@ namespace pdr { n.pt().add_property(ncore, uses_level?n.level():infty_level); } CASSERT("pdr",n.level() == 0 || check_invariant(n.level()-1)); - m_search.backtrack_level(!found_invariant && m_params.flexible_trace(), n); + m_search.backtrack_level(!found_invariant && m_params.pdr_flexible_trace(), n); break; } case l_undef: { @@ -1924,7 +2085,7 @@ namespace pdr { } void context::propagate(unsigned max_prop_lvl) { - if (m_params.simplify_formulas_pre()) { + if (m_params.pdr_simplify_formulas_pre()) { simplify_formulas(); } for (unsigned lvl = m_expanded_lvl; lvl <= max_prop_lvl; lvl++) { @@ -1943,7 +2104,7 @@ namespace pdr { throw inductive_exception(); } } - if (m_params.simplify_formulas_post()) { + if (m_params.pdr_simplify_formulas_post()) { simplify_formulas(); } } @@ -1991,16 +2152,18 @@ namespace pdr { */ void context::create_children(model_node& n) { SASSERT(n.level() > 0); - bool use_model_generalizer = m_params.use_model_generalizer(); + bool use_model_generalizer = m_params.pdr_use_model_generalizer(); scoped_no_proof _sc(m); pred_transformer& pt = n.pt(); model_ref M = n.get_model_ptr(); + SASSERT(M.get()); datalog::rule const& r = pt.find_rule(*M); expr* T = pt.get_transition(r); expr* phi = n.state(); n.set_rule(&r); + TRACE("pdr", tout << "Model:\n"; @@ -2009,11 +2172,11 @@ namespace pdr { tout << "Transition:\n" << mk_pp(T, m) << "\n"; tout << "Phi:\n" << mk_pp(phi, m) << "\n";); - model_evaluator mev(m); + model_implicant mev(m); expr_ref_vector mdl(m), forms(m), Phi(m); forms.push_back(T); forms.push_back(phi); - qe::flatten_and(forms); + flatten_and(forms); ptr_vector forms1(forms.size(), forms.c_ptr()); if (use_model_generalizer) { Phi.append(mev.minimize_model(forms1, M)); @@ -2069,7 +2232,7 @@ namespace pdr { TRACE("pdr", tout << "Projected:\n" << mk_pp(phi1, m) << "\n";); } Phi.reset(); - qe::flatten_and(phi1, Phi); + flatten_and(phi1, Phi); unsigned_vector indices; vector child_states; child_states.resize(preds.size(), expr_ref_vector(m)); @@ -2115,10 +2278,10 @@ namespace pdr { model_node* child = alloc(model_node, &n, n_cube, pt, n.level()-1); ++m_stats.m_num_nodes; m_search.add_leaf(*child); - IF_VERBOSE(3, verbose_stream() << "Predecessor: " << mk_pp(o_cube, m) << "\n";); + IF_VERBOSE(2, verbose_stream() << "Predecessor: " << mk_pp(n_cube, m) << " " << (child->is_closed()?"closed":"open") << "\n";); m_stats.m_max_depth = std::max(m_stats.m_max_depth, child->depth()); } - check_pre_closed(n); + n.check_pre_closed(); TRACE("pdr", m_search.display(tout);); } diff --git a/src/muz/pdr/pdr_context.h b/src/muz/pdr/pdr_context.h index e21f46412..7cba1e0c5 100644 --- a/src/muz/pdr/pdr_context.h +++ b/src/muz/pdr/pdr_context.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _PDR_CONTEXT_H_ -#define _PDR_CONTEXT_H_ +#ifndef PDR_CONTEXT_H_ +#define PDR_CONTEXT_H_ #ifdef _CYGWIN #undef min @@ -124,6 +124,7 @@ namespace pdr { unsigned get_num_levels() { return m_levels.size(); } expr_ref get_cover_delta(func_decl* p_orig, int level); void add_cover(unsigned level, expr* property); + context& get_context() { return ctx; } std::ostream& display(std::ostream& strm) const; @@ -185,6 +186,8 @@ namespace pdr { // structure for counter-example search. class model_node { model_node* m_parent; + model_node* m_next; + model_node* m_prev; pred_transformer& m_pt; expr_ref m_state; model_ref m_model; @@ -196,13 +199,17 @@ namespace pdr { datalog::rule const* m_rule; public: model_node(model_node* parent, expr_ref& state, pred_transformer& pt, unsigned level): - m_parent(parent), m_pt(pt), m_state(state), m_model(0), - m_level(level), m_orig_level(level), m_depth(0), m_closed(false), m_rule(0) { - if (m_parent) { - m_parent->m_children.push_back(this); - SASSERT(m_parent->m_level == level+1); - SASSERT(m_parent->m_level > 0); - m_depth = m_parent->m_depth+1; + m_parent(parent), m_next(0), m_prev(0), m_pt(pt), m_state(state), m_model(0), + m_level(level), m_orig_level(level), m_depth(0), m_closed(false), m_rule(0) { + model_node* p = m_parent; + if (p) { + p->m_children.push_back(this); + SASSERT(p->m_level == level+1); + SASSERT(p->m_level > 0); + m_depth = p->m_depth+1; + if (p && p->is_closed()) { + p->set_open(); + } } } void set_model(model_ref& m) { m_model = m; } @@ -210,7 +217,7 @@ namespace pdr { unsigned orig_level() const { return m_orig_level; } unsigned depth() const { return m_depth; } void increase_level() { ++m_level; } - expr* state() const { return m_state; } + expr_ref const& state() const { return m_state; } ptr_vector const& children() { return m_children; } pred_transformer& pt() const { return m_pt; } model_node* parent() const { return m_parent; } @@ -230,8 +237,9 @@ namespace pdr { return true; } + void check_pre_closed(); void set_closed(); - void reopen(); + void set_open(); void set_pre_closed() { m_closed = true; } void reset() { m_children.reset(); } @@ -241,37 +249,45 @@ namespace pdr { void mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding); std::ostream& display(std::ostream& out, unsigned indent); + + void dequeue(model_node*& root); + void enqueue(model_node* n); + model_node* next() const { return m_next; } + bool is_goal() const { return 0 != next(); } }; class model_search { typedef ptr_vector model_nodes; bool m_bfs; model_node* m_root; - std::deque m_leaves; + model_node* m_goal; vector > m_cache; - obj_map& cache(model_node const& n); void erase_children(model_node& n, bool backtrack); - void erase_leaf(model_node& n); void remove_node(model_node& n, bool backtrack); - void enqueue_leaf(model_node& n); // add leaf to priority queue. + void enqueue_leaf(model_node* n); // add leaf to priority queue. void update_models(); + void set_leaf(model_node& n); // Set node as leaf, remove children. + bool is_repeated(model_node& n) const; + unsigned num_goals() const; + public: - model_search(bool bfs): m_bfs(bfs), m_root(0) {} + model_search(bool bfs): m_bfs(bfs), m_root(0), m_goal(0) {} ~model_search(); void reset(); model_node* next(); - bool is_repeated(model_node& n) const; void add_leaf(model_node& n); // add fresh node. - void set_leaf(model_node& n); // Set node as leaf, remove children. - + void set_root(model_node* n); model_node& get_root() const { return *m_root; } std::ostream& display(std::ostream& out) const; - expr_ref get_trace(context const& ctx); + expr_ref get_trace(context const& ctx); proof_ref get_proof_trace(context const& ctx); void backtrack_level(bool uses_level, model_node& n); + void remove_goal(model_node& n); + + void well_formed(); }; struct model_exception { }; @@ -357,6 +373,9 @@ namespace pdr { void reset_core_generalizers(); void validate(); + void validate_proof(); + void validate_search(); + void validate_model(); public: diff --git a/src/muz/pdr/pdr_dl_interface.cpp b/src/muz/pdr/pdr_dl_interface.cpp index 45872fe99..0a05d1ff3 100644 --- a/src/muz/pdr/pdr_dl_interface.cpp +++ b/src/muz/pdr/pdr_dl_interface.cpp @@ -84,10 +84,11 @@ lbool dl_interface::query(expr * query) { m_pred2slice.reset(); ast_manager& m = m_ctx.get_manager(); datalog::rule_manager& rm = m_ctx.get_rule_manager(); + datalog::rule_set& rules0 = m_ctx.get_rules(); - datalog::rule_set old_rules(m_ctx.get_rules()); + datalog::rule_set old_rules(rules0); func_decl_ref query_pred(m); - rm.mk_query(query, m_ctx.get_rules()); + rm.mk_query(query, rules0); expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); @@ -105,7 +106,7 @@ lbool dl_interface::query(expr * query) { apply_default_transformation(m_ctx); - if (m_ctx.get_params().slice()) { + if (m_ctx.get_params().xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); @@ -122,12 +123,12 @@ lbool dl_interface::query(expr * query) { } } - if (m_ctx.get_params().unfold_rules() > 0) { - unsigned num_unfolds = m_ctx.get_params().unfold_rules(); + if (m_ctx.get_params().xform_unfold_rules() > 0) { + unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules(); datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); - if (m_ctx.get_params().coalesce_rules()) { + if (m_ctx.get_params().xform_coalesce_rules()) { m_ctx.transform_rules(transf1); } while (num_unfolds > 0) { @@ -136,15 +137,16 @@ lbool dl_interface::query(expr * query) { } } - if (m_ctx.get_rules().get_output_predicates().empty()) { + const datalog::rule_set& rules = m_ctx.get_rules(); + if (rules.get_output_predicates().empty()) { m_context->set_unsat(); return l_false; } - query_pred = m_ctx.get_rules().get_output_predicate(); + query_pred = rules.get_output_predicate(); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); - m_pdr_rules.replace_rules(m_ctx.get_rules()); + m_pdr_rules.replace_rules(rules); m_pdr_rules.close(); m_ctx.record_transformed_rules(); m_ctx.reopen(); @@ -176,7 +178,7 @@ expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig) { } void dl_interface::add_cover(int level, func_decl* pred, expr* property) { - if (m_ctx.get_params().slice()) { + if (m_ctx.get_params().xform_slice()) { throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers"); } m_context->add_cover(level, pred, property); diff --git a/src/muz/pdr/pdr_dl_interface.h b/src/muz/pdr/pdr_dl_interface.h index 610e7fe06..f9e54d85b 100644 --- a/src/muz/pdr/pdr_dl_interface.h +++ b/src/muz/pdr/pdr_dl_interface.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _PDR_DL_INTERFACE_H_ -#define _PDR_DL_INTERFACE_H_ +#ifndef PDR_DL_INTERFACE_H_ +#define PDR_DL_INTERFACE_H_ #include "lbool.h" #include "dl_rule.h" diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index 7c38bf86f..94265e95d 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -34,8 +34,6 @@ Revision History: #include "proof_utils.h" #include "reg_decl_plugins.h" -#define PROOF_MODE PGM_FINE -//#define PROOF_MODE PGM_COARSE namespace pdr { @@ -374,7 +372,7 @@ namespace pdr { farkas_learner::farkas_learner(smt_params& params, ast_manager& outer_mgr) : m_proof_params(get_proof_params(params)), - m_pr(PROOF_MODE), + m_pr(PGM_FINE), m_constr(0), m_combine_farkas_coefficients(true), p2o(m_pr, outer_mgr), @@ -451,7 +449,7 @@ namespace pdr { expr_set bs; expr_ref_vector blist(m_pr); - qe::flatten_and(B, blist); + flatten_and(B, blist); for (unsigned i = 0; i < blist.size(); ++i) { bs.insert(blist[i].get()); } diff --git a/src/muz/pdr/pdr_farkas_learner.h b/src/muz/pdr/pdr_farkas_learner.h index 546759a33..3a4d81f96 100644 --- a/src/muz/pdr/pdr_farkas_learner.h +++ b/src/muz/pdr/pdr_farkas_learner.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _PDR_FARKAS_LEARNER_H_ -#define _PDR_FARKAS_LEARNER_H_ +#ifndef PDR_FARKAS_LEARNER_H_ +#define PDR_FARKAS_LEARNER_H_ #include "arith_decl_plugin.h" #include "ast_translation.h" diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index 7c2557260..fd700cfd7 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -119,7 +119,7 @@ namespace pdr { if (core.empty()) return; expr_ref A(m), B(qe::mk_and(core)), C(m); expr_ref_vector Bs(m); - qe::flatten_or(B, Bs); + flatten_or(B, Bs); A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level()); bool change = false; @@ -138,7 +138,7 @@ namespace pdr { C = qe::mk_or(Bs); TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";); core.reset(); - qe::flatten_and(C, core); + flatten_and(C, core); uses_level = true; } } @@ -190,7 +190,7 @@ namespace pdr { expr_ref fml2 = n.pt().get_formulas(n.level(), false); fml1_2.push_back(fml1); fml1_2.push_back(0); - qe::flatten_and(fml2, fmls); + flatten_and(fml2, fmls); for (unsigned i = 0; i < fmls.size(); ++i) { fml2 = m.mk_not(fmls[i].get()); fml1_2[1] = fml2; @@ -558,7 +558,6 @@ namespace pdr { { expr_ref_vector conj(m), sub(m); expr_ref result(m); - ptr_vector sorts; svector names; unsigned ut_size = rule.get_uninterpreted_tail_size(); unsigned t_size = rule.get_tail_size(); @@ -599,16 +598,15 @@ namespace pdr { expr_ref tmp = result; var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result); } - get_free_vars(result, sorts); - for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } - names.push_back(symbol(sorts.size() - i - 1)); + expr_free_vars fv; + fv(result); + fv.set_default_sort(m.mk_bool_sort()); + for (unsigned i = 0; i < fv.size(); ++i) { + names.push_back(symbol(fv.size() - i - 1)); } - if (!sorts.empty()) { - sorts.reverse(); - result = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), result); + if (!fv.empty()) { + fv.reverse(); + result = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), result); } return result; } diff --git a/src/muz/pdr/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h index be04ec646..3e4576fa1 100644 --- a/src/muz/pdr/pdr_generalizers.h +++ b/src/muz/pdr/pdr_generalizers.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _PDR_GENERALIZERS_H_ -#define _PDR_GENERALIZERS_H_ +#ifndef PDR_GENERALIZERS_H_ +#define PDR_GENERALIZERS_H_ #include "pdr_context.h" #include "pdr_closure.h" diff --git a/src/muz/pdr/pdr_manager.cpp b/src/muz/pdr/pdr_manager.cpp index bda54dbd7..1e808c455 100644 --- a/src/muz/pdr/pdr_manager.cpp +++ b/src/muz/pdr/pdr_manager.cpp @@ -56,7 +56,7 @@ namespace pdr { expr_ref inductive_property::fixup_clause(expr* fml) const { expr_ref_vector disjs(m); - qe::flatten_or(fml, disjs); + flatten_or(fml, disjs); expr_ref result(m); bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result); return result; @@ -65,7 +65,7 @@ namespace pdr { expr_ref inductive_property::fixup_clauses(expr* fml) const { expr_ref_vector conjs(m); expr_ref result(m); - qe::flatten_and(fml, conjs); + flatten_and(fml, conjs); for (unsigned i = 0; i < conjs.size(); ++i) { conjs[i] = fixup_clause(conjs[i].get()); } @@ -119,7 +119,7 @@ namespace pdr { } - void inductive_property::display(ptr_vector const& rules, std::ostream& out) const { + void inductive_property::display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const { func_decl_set bound_decls, aux_decls; collect_decls_proc collect_decls(bound_decls, aux_decls); @@ -153,7 +153,7 @@ namespace pdr { for (unsigned i = 0; i < rules.size(); ++i) { out << "(push)\n"; out << "(assert (not\n"; - rules[i]->display_smt2(m, out); + rm.display_smt2(*rules[i], out); out << "))\n"; out << "(check-sat)\n"; out << "(pop)\n"; @@ -237,7 +237,7 @@ namespace pdr { expr_ref manager::mk_not_and(expr_ref_vector const& conjs) { expr_ref result(m), e(m); expr_ref_vector es(conjs); - qe::flatten_and(es); + flatten_and(es); for (unsigned i = 0; i < es.size(); ++i) { m_brwr.mk_not(es[i].get(), e); es[i] = e; diff --git a/src/muz/pdr/pdr_manager.h b/src/muz/pdr/pdr_manager.h index 0e8e890e8..9049e4ca9 100644 --- a/src/muz/pdr/pdr_manager.h +++ b/src/muz/pdr/pdr_manager.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef _PDR_MANAGER_H_ -#define _PDR_MANAGER_H_ +#ifndef PDR_MANAGER_H_ +#define PDR_MANAGER_H_ #include #include @@ -70,8 +70,8 @@ namespace pdr { expr_ref to_expr() const; void to_model(model_ref& md) const; - - void display(ptr_vector const& rules, std::ostream& out) const; + + void display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const; }; class manager diff --git a/src/muz/pdr/pdr_prop_solver.cpp b/src/muz/pdr/pdr_prop_solver.cpp index 8fe8c0e0e..8c407161e 100644 --- a/src/muz/pdr/pdr_prop_solver.cpp +++ b/src/muz/pdr/pdr_prop_solver.cpp @@ -30,7 +30,6 @@ Revision History: #include "pdr_farkas_learner.h" #include "ast_smt2_pp.h" #include "expr_replacer.h" -#include "fixedpoint_params.hpp" // // Auxiliary structure to introduce propositional names for assumptions that are not @@ -77,7 +76,7 @@ namespace pdr { } void mk_safe(expr_ref_vector& conjs) { - qe::flatten_and(conjs); + flatten_and(conjs); expand_literals(conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr * lit = conjs[i].get(); @@ -226,12 +225,11 @@ namespace pdr { }; - prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& name) : + prop_solver::prop_solver(manager& pm, symbol const& name) : m_fparams(pm.get_fparams()), m(pm.get_manager()), m_pm(pm), m_name(name), - m_try_minimize_core(p.try_minimize_core()), m_ctx(pm.mk_fresh()), m_pos_level_atoms(m), m_neg_level_atoms(m), diff --git a/src/muz/pdr/pdr_prop_solver.h b/src/muz/pdr/pdr_prop_solver.h index a63ec2bf4..ad7e1fd9a 100644 --- a/src/muz/pdr/pdr_prop_solver.h +++ b/src/muz/pdr/pdr_prop_solver.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _PROP_SOLVER_H_ -#define _PROP_SOLVER_H_ +#ifndef PROP_SOLVER_H_ +#define PROP_SOLVER_H_ #include #include @@ -31,7 +31,6 @@ Revision History: #include "pdr_manager.h" #include "pdr_smt_context_manager.h" -struct fixedpoint_params; namespace pdr { class prop_solver { @@ -41,7 +40,6 @@ namespace pdr { ast_manager& m; manager& m_pm; symbol m_name; - bool m_try_minimize_core; scoped_ptr m_ctx; decl_vector m_level_preds; app_ref_vector m_pos_level_atoms; // atoms used to identify level @@ -75,7 +73,7 @@ namespace pdr { public: - prop_solver(pdr::manager& pm, fixedpoint_params const& p, symbol const& name); + prop_solver(pdr::manager& pm, symbol const& name); /** return true is s is a symbol introduced by prop_solver */ bool is_aux_symbol(func_decl * s) const { diff --git a/src/muz/pdr/pdr_reachable_cache.h b/src/muz/pdr/pdr_reachable_cache.h index aef6f7a4f..66832ba42 100644 --- a/src/muz/pdr/pdr_reachable_cache.h +++ b/src/muz/pdr/pdr_reachable_cache.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef _REACHABLE_CACHE_H_ -#define _REACHABLE_CACHE_H_ +#ifndef REACHABLE_CACHE_H_ +#define REACHABLE_CACHE_H_ #include "ast.h" #include "ref_vector.h" #include "pdr_manager.h" diff --git a/src/muz/pdr/pdr_smt_context_manager.h b/src/muz/pdr/pdr_smt_context_manager.h index 4775dc58f..173367a41 100644 --- a/src/muz/pdr/pdr_smt_context_manager.h +++ b/src/muz/pdr/pdr_smt_context_manager.h @@ -17,11 +17,10 @@ Revision History: --*/ -#ifndef _PDR_SMT_CONTEXT_MANAGER_H_ -#define _PDR_SMT_CONTEXT_MANAGER_H_ +#ifndef PDR_SMT_CONTEXT_MANAGER_H_ +#define PDR_SMT_CONTEXT_MANAGER_H_ #include "smt_kernel.h" -#include "sat_solver.h" #include "func_decl_dependencies.h" #include "dl_util.h" @@ -71,23 +70,6 @@ namespace pdr { virtual expr* get_unsat_core_expr(unsigned i) { return m_context.get_unsat_core_expr(i); } }; - // TBD: - class sat_context : public smt_context { - sat::solver m_solver; - public: - sat_context(smt::kernel & ctx, smt_context_manager& p, app* pred); - virtual ~sat_context() {} - virtual void assert_expr(expr* e); - virtual lbool check(expr_ref_vector& assumptions); - virtual void get_model(model_ref& model); - virtual proof* get_proof(); - virtual void pop() { m_solver.pop(1); } - virtual void push() { m_solver.push(); } - // TBD: add unsat core extraction with sat::solver. - virtual unsigned get_unsat_core_size(); - virtual expr* get_unsat_core_expr(unsigned i); - }; - class smt_context_manager { smt_params& m_fparams; ast_manager& m; diff --git a/src/muz/pdr/pdr_sym_mux.h b/src/muz/pdr/pdr_sym_mux.h index c8ca56c7a..986d961c7 100644 --- a/src/muz/pdr/pdr_sym_mux.h +++ b/src/muz/pdr/pdr_sym_mux.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _SYM_MUX_H_ -#define _SYM_MUX_H_ +#ifndef SYM_MUX_H_ +#define SYM_MUX_H_ #include "ast.h" #include "map.h" diff --git a/src/muz/pdr/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp index f122f1e2c..f8758d997 100644 --- a/src/muz/pdr/pdr_util.cpp +++ b/src/muz/pdr/pdr_util.cpp @@ -90,883 +90,10 @@ namespace pdr { return res.str(); } - - - ///////////////////////// - // model_evaluator - // - - - void model_evaluator::assign_value(expr* e, expr* val) { - rational r; - if (m.is_true(val)) { - set_true(e); - } - else if (m.is_false(val)) { - set_false(e); - } - else if (m_arith.is_numeral(val, r)) { - set_number(e, r); - } - else if (m.is_value(val)) { - set_value(e, val); - } - else { - IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); - TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";); - set_x(e); - } - } - - void model_evaluator::setup_model(model_ref& model) { - m_numbers.reset(); - m_values.reset(); - m_model = model; - rational r; - unsigned sz = model->get_num_constants(); - for (unsigned i = 0; i < sz; i++) { - func_decl * d = model->get_constant(i); - expr* val = model->get_const_interp(d); - expr* e = m.mk_const(d); - m_refs.push_back(e); - assign_value(e, val); - } - } - - void model_evaluator::reset() { - m1.reset(); - m2.reset(); - m_values.reset(); - m_visited.reset(); - m_numbers.reset(); - m_refs.reset(); - m_model = 0; - } - - expr_ref_vector model_evaluator::minimize_model(ptr_vector const & formulas, model_ref& mdl) { - setup_model(mdl); - - TRACE("pdr_verbose", - tout << "formulas:\n"; - for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; - ); - - expr_ref_vector model = prune_by_cone_of_influence(formulas); - TRACE("pdr_verbose", - tout << "pruned model:\n"; - for (unsigned i = 0; i < model.size(); ++i) tout << mk_pp(model[i].get(), m) << "\n";); - - reset(); - - DEBUG_CODE( - setup_model(mdl); - VERIFY(check_model(formulas)); - reset();); - - return model; - } - - expr_ref_vector model_evaluator::minimize_literals(ptr_vector const& formulas, model_ref& mdl) { - - TRACE("pdr", - tout << "formulas:\n"; - for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; - ); - - expr_ref_vector result(m); - expr_ref tmp(m); - ptr_vector tocollect; - - setup_model(mdl); - collect(formulas, tocollect); - for (unsigned i = 0; i < tocollect.size(); ++i) { - expr* e = tocollect[i]; - expr* e1, *e2; - SASSERT(m.is_bool(e)); - SASSERT(is_true(e) || is_false(e)); - if (is_true(e)) { - result.push_back(e); - } - // hack to break disequalities for arithmetic variables. - else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) { - if (get_number(e1) < get_number(e2)) { - result.push_back(m_arith.mk_lt(e1,e2)); - } - else { - result.push_back(m_arith.mk_lt(e2,e1)); - } - } - else { - result.push_back(m.mk_not(e)); - } - } - reset(); - TRACE("pdr", - tout << "minimized model:\n"; - for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n"; - ); - - return result; - } - - void model_evaluator::process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect) { - SASSERT(m.is_bool(e)); - SASSERT(is_true(e) || is_false(e)); - unsigned v = is_true(e); - unsigned sz = e->get_num_args(); - expr* const* args = e->get_args(); - if (e->get_family_id() == m.get_basic_family_id()) { - switch(e->get_decl_kind()) { - case OP_TRUE: - break; - case OP_FALSE: - break; - case OP_EQ: - case OP_IFF: - if (args[0] == args[1]) { - SASSERT(v); - // no-op - } - else if (m.is_bool(args[0])) { - todo.append(sz, args); - } - else { - tocollect.push_back(e); - } - break; - case OP_DISTINCT: - tocollect.push_back(e); - break; - case OP_ITE: - if (args[1] == args[2]) { - tocollect.push_back(args[1]); - } - else if (is_true(args[1]) && is_true(args[2])) { - todo.append(2, args+1); - } - else if (is_false(args[1]) && is_false(args[2])) { - todo.append(2, args+1); - } - else if (is_true(args[0])) { - todo.append(2, args); - } - else { - SASSERT(is_false(args[0])); - todo.push_back(args[0]); - todo.push_back(args[2]); - } - break; - case OP_AND: - if (v) { - todo.append(sz, args); - } - else { - unsigned i = 0; - for (; !is_false(args[i]) && i < sz; ++i); - if (i == sz) { - fatal_error(1); - } - VERIFY(i < sz); - todo.push_back(args[i]); - } - break; - case OP_OR: - if (v) { - unsigned i = 0; - for (; !is_true(args[i]) && i < sz; ++i); - if (i == sz) { - fatal_error(1); - } - VERIFY(i < sz); - todo.push_back(args[i]); - } - else { - todo.append(sz, args); - } - break; - case OP_XOR: - case OP_NOT: - todo.append(sz, args); - break; - case OP_IMPLIES: - if (v) { - if (is_true(args[1])) { - todo.push_back(args[1]); - } - else if (is_false(args[0])) { - todo.push_back(args[0]); - } - else { - IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); - UNREACHABLE(); - } - } - else { - todo.append(sz, args); - } - break; - default: - IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); - UNREACHABLE(); - } - } - else { - tocollect.push_back(e); - } - } - - void model_evaluator::collect(ptr_vector const& formulas, ptr_vector& tocollect) { - ptr_vector todo; - todo.append(formulas); - m_visited.reset(); - - VERIFY(check_model(formulas)); - - while (!todo.empty()) { - app* e = to_app(todo.back()); - todo.pop_back(); - if (!m_visited.is_marked(e)) { - process_formula(e, todo, tocollect); - m_visited.mark(e, true); - } - } - m_visited.reset(); - } - - expr_ref_vector model_evaluator::prune_by_cone_of_influence(ptr_vector const & formulas) { - ptr_vector tocollect; - collect(formulas, tocollect); - m1.reset(); - m2.reset(); - for (unsigned i = 0; i < tocollect.size(); ++i) { - TRACE("pdr_verbose", tout << "collect: " << mk_pp(tocollect[i], m) << "\n";); - for_each_expr(*this, m_visited, tocollect[i]); - } - unsigned sz = m_model->get_num_constants(); - expr_ref e(m), eq(m), val(m); - expr_ref_vector model(m); - for (unsigned i = 0; i < sz; i++) { - e = m.mk_const(m_model->get_constant(i)); - if (m_visited.is_marked(e)) { - val = eval(m_model, e); - eq = m.mk_eq(e, val); - model.push_back(eq); - } - } - m_visited.reset(); - TRACE("pdr", tout << sz << " ==> " << model.size() << "\n";); - return model; - - } - - void model_evaluator::eval_arith(app* e) { - rational r, r2; - -#define ARG1 e->get_arg(0) -#define ARG2 e->get_arg(1) - - unsigned arity = e->get_num_args(); - for (unsigned i = 0; i < arity; ++i) { - expr* arg = e->get_arg(i); - if (is_x(arg)) { - set_x(e); - return; - } - SASSERT(!is_unknown(arg)); - } - switch(e->get_decl_kind()) { - case OP_NUM: - VERIFY(m_arith.is_numeral(e, r)); - set_number(e, r); - break; - case OP_IRRATIONAL_ALGEBRAIC_NUM: - set_x(e); - break; - case OP_LE: - set_bool(e, get_number(ARG1) <= get_number(ARG2)); - break; - case OP_GE: - set_bool(e, get_number(ARG1) >= get_number(ARG2)); - break; - case OP_LT: - set_bool(e, get_number(ARG1) < get_number(ARG2)); - break; - case OP_GT: - set_bool(e, get_number(ARG1) > get_number(ARG2)); - break; - case OP_ADD: - r = rational::zero(); - for (unsigned i = 0; i < arity; ++i) { - r += get_number(e->get_arg(i)); - } - set_number(e, r); - break; - case OP_SUB: - r = get_number(e->get_arg(0)); - for (unsigned i = 1; i < arity; ++i) { - r -= get_number(e->get_arg(i)); - } - set_number(e, r); - break; - case OP_UMINUS: - SASSERT(arity == 1); - set_number(e, get_number(e->get_arg(0))); - break; - case OP_MUL: - r = rational::one(); - for (unsigned i = 0; i < arity; ++i) { - r *= get_number(e->get_arg(i)); - } - set_number(e, r); - break; - case OP_DIV: - SASSERT(arity == 2); - r = get_number(ARG2); - if (r.is_zero()) { - set_x(e); - } - else { - set_number(e, get_number(ARG1) / r); - } - break; - case OP_IDIV: - SASSERT(arity == 2); - r = get_number(ARG2); - if (r.is_zero()) { - set_x(e); - } - else { - set_number(e, div(get_number(ARG1), r)); - } - break; - case OP_REM: - // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2) - SASSERT(arity == 2); - r = get_number(ARG2); - if (r.is_zero()) { - set_x(e); - } - else { - r2 = mod(get_number(ARG1), r); - if (r.is_neg()) r2.neg(); - set_number(e, r2); - } - break; - case OP_MOD: - SASSERT(arity == 2); - r = get_number(ARG2); - if (r.is_zero()) { - set_x(e); - } - else { - set_number(e, mod(get_number(ARG1), r)); - } - break; - case OP_TO_REAL: - SASSERT(arity == 1); - set_number(e, get_number(ARG1)); - break; - case OP_TO_INT: - SASSERT(arity == 1); - set_number(e, floor(get_number(ARG1))); - break; - case OP_IS_INT: - SASSERT(arity == 1); - set_bool(e, get_number(ARG1).is_int()); - break; - case OP_POWER: - set_x(e); - break; - default: - IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); - UNREACHABLE(); - break; - } - } - - void model_evaluator::inherit_value(expr* e, expr* v) { - expr* w; - SASSERT(!is_unknown(v)); - SASSERT(m.get_sort(e) == m.get_sort(v)); - if (is_x(v)) { - set_x(e); - } - else if (m.is_bool(e)) { - SASSERT(m.is_bool(v)); - if (is_true(v)) set_true(e); - else if (is_false(v)) set_false(e); - else { - TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); - set_x(e); - } - } - else if (m_arith.is_int_real(e)) { - set_number(e, get_number(v)); - } - else if (m.is_value(v)) { - set_value(e, v); - } - else if (m_values.find(v, w)) { - set_value(e, w); - } - else { - TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); - set_x(e); - } - } - - void model_evaluator::eval_exprs(expr_ref_vector& es) { - model_ref mr(m_model); - for (unsigned j = 0; j < es.size(); ++j) { - if (m_array.is_as_array(es[j].get())) { - es[j] = eval(mr, es[j].get()); - } - } - } - - bool model_evaluator::extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case) { - SASSERT(m_array.is_array(a)); - - TRACE("pdr", tout << mk_pp(a, m) << "\n";); - while (m_array.is_store(a)) { - expr_ref_vector store(m); - store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); - eval_exprs(store); - stores.push_back(store); - a = to_app(a)->get_arg(0); - } - - if (m_array.is_const(a)) { - else_case = to_app(a)->get_arg(0); - return true; - } - - while (m_array.is_as_array(a)) { - func_decl* f = m_array.get_as_array_func_decl(to_app(a)); - func_interp* g = m_model->get_func_interp(f); - unsigned sz = g->num_entries(); - unsigned arity = f->get_arity(); - for (unsigned i = 0; i < sz; ++i) { - expr_ref_vector store(m); - func_entry const* fe = g->get_entry(i); - store.append(arity, fe->get_args()); - store.push_back(fe->get_result()); - for (unsigned j = 0; j < store.size(); ++j) { - if (!is_ground(store[j].get())) { - TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); - return false; - } - } - eval_exprs(store); - stores.push_back(store); - } - else_case = g->get_else(); - if (!else_case) { - TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";); - return false; - } - if (!is_ground(else_case)) { - TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); - return false; - } - if (m_array.is_as_array(else_case)) { - model_ref mr(m_model); - else_case = eval(mr, else_case); - } - TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";); - return true; - } - TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";); - - return false; - } - - /** - best effort evaluator of extensional array equality. - */ - void model_evaluator::eval_array_eq(app* e, expr* arg1, expr* arg2) { - TRACE("pdr", tout << "array equality: " << mk_pp(e, m) << "\n";); - expr_ref v1(m), v2(m); - m_model->eval(arg1, v1); - m_model->eval(arg2, v2); - if (v1 == v2) { - set_true(e); - return; - } - sort* s = m.get_sort(arg1); - sort* r = get_array_range(s); - // give up evaluating finite domain/range arrays - if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { - TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); - set_x(e); - return; - } - vector store; - expr_ref else1(m), else2(m); - if (!extract_array_func_interp(v1, store, else1) || - !extract_array_func_interp(v2, store, else2)) { - TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); - set_x(e); - return; - } - - if (else1 != else2) { - if (m.is_value(else1) && m.is_value(else2)) { - TRACE("pdr", tout - << "defaults are different: " << mk_pp(e, m) << " " - << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); - set_false(e); - } - else if (m_array.is_array(else1)) { - eval_array_eq(e, else1, else2); - } - else { - TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); - set_x(e); - } - return; - } - - expr_ref s1(m), s2(m), w1(m), w2(m); - expr_ref_vector args1(m), args2(m); - args1.push_back(v1); - args2.push_back(v2); - for (unsigned i = 0; i < store.size(); ++i) { - args1.resize(1); - args2.resize(1); - args1.append(store[i].size()-1, store[i].c_ptr()); - args2.append(store[i].size()-1, store[i].c_ptr()); - s1 = m_array.mk_select(args1.size(), args1.c_ptr()); - s2 = m_array.mk_select(args2.size(), args2.c_ptr()); - m_model->eval(s1, w1); - m_model->eval(s2, w2); - if (w1 == w2) { - continue; - } - if (m.is_value(w1) && m.is_value(w2)) { - TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; - tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; - tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); - set_false(e); - } - else if (m_array.is_array(w1)) { - eval_array_eq(e, w1, w2); - if (is_true(e)) { - continue; - } - } - else { - TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); - set_x(e); - } - return; - } - set_true(e); - } - - void model_evaluator::eval_eq(app* e, expr* arg1, expr* arg2) { - if (arg1 == arg2) { - set_true(e); - } - else if (m_array.is_array(arg1)) { - eval_array_eq(e, arg1, arg2); - } - else if (is_x(arg1) || is_x(arg2)) { - expr_ref eq(m), vl(m); - eq = m.mk_eq(arg1, arg2); - m_model->eval(eq, vl); - if (m.is_true(vl)) { - set_bool(e, true); - } - else if (m.is_false(vl)) { - set_bool(e, false); - } - else { - TRACE("pdr", tout << "cannot evaluate: " << mk_pp(vl, m) << "\n";); - set_x(e); - } - } - else if (m.is_bool(arg1)) { - bool val = is_true(arg1) == is_true(arg2); - SASSERT(val == (is_false(arg1) == is_false(arg2))); - if (val) { - set_true(e); - } - else { - set_false(e); - } - } - else if (m_arith.is_int_real(arg1)) { - set_bool(e, get_number(arg1) == get_number(arg2)); - } - else { - expr* e1 = get_value(arg1); - expr* e2 = get_value(arg2); - if (m.is_value(e1) && m.is_value(e2)) { - set_bool(e, e1 == e2); - } - else if (e1 == e2) { - set_bool(e, true); - } - else { - TRACE("pdr", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";); - set_x(e); - } - } - } - - void model_evaluator::eval_basic(app* e) { - expr* arg1, *arg2; - expr *argCond, *argThen, *argElse, *arg; - bool has_x = false; - unsigned arity = e->get_num_args(); - switch(e->get_decl_kind()) { - case OP_AND: - for (unsigned j = 0; j < arity; ++j) { - expr * arg = e->get_arg(j); - if (is_false(arg)) { - set_false(e); - return; - } - else if (is_x(arg)) { - has_x = true; - } - else { - SASSERT(is_true(arg)); - } - } - if (has_x) { - set_x(e); - } - else { - set_true(e); - } - break; - case OP_OR: - for (unsigned j = 0; j < arity; ++j) { - expr * arg = e->get_arg(j); - if (is_true(arg)) { - set_true(e); - return; - } - else if (is_x(arg)) { - has_x = true; - } - else { - SASSERT(is_false(arg)); - } - } - if (has_x) { - set_x(e); - } - else { - set_false(e); - } - break; - case OP_NOT: - VERIFY(m.is_not(e, arg)); - if (is_true(arg)) { - set_false(e); - } - else if (is_false(arg)) { - set_true(e); - } - else { - SASSERT(is_x(arg)); - set_x(e); - } - break; - case OP_IMPLIES: - VERIFY(m.is_implies(e, arg1, arg2)); - if (is_false(arg1) || is_true(arg2)) { - set_true(e); - } - else if (arg1 == arg2) { - set_true(e); - } - else if (is_true(arg1) && is_false(arg2)) { - set_false(e); - } - else { - SASSERT(is_x(arg1) || is_x(arg2)); - set_x(e); - } - break; - case OP_IFF: - VERIFY(m.is_iff(e, arg1, arg2)); - eval_eq(e, arg1, arg2); - break; - case OP_ITE: - VERIFY(m.is_ite(e, argCond, argThen, argElse)); - if (is_true(argCond)) { - inherit_value(e, argThen); - } - else if (is_false(argCond)) { - inherit_value(e, argElse); - } - else if (argThen == argElse) { - inherit_value(e, argThen); - } - else if (m.is_bool(e)) { - SASSERT(is_x(argCond)); - if (is_x(argThen) || is_x(argElse)) { - set_x(e); - } - else if (is_true(argThen) == is_true(argElse)) { - inherit_value(e, argThen); - } - else { - set_x(e); - } - } - else { - set_x(e); - } - break; - case OP_TRUE: - set_true(e); - break; - case OP_FALSE: - set_false(e); - break; - case OP_EQ: - VERIFY(m.is_eq(e, arg1, arg2)); - eval_eq(e, arg1, arg2); - break; - case OP_DISTINCT: { - vector values; - for (unsigned i = 0; i < arity; ++i) { - expr* arg = e->get_arg(i); - if (is_x(arg)) { - set_x(e); - return; - } - values.push_back(get_number(arg)); - } - std::sort(values.begin(), values.end()); - for (unsigned i = 0; i + 1 < values.size(); ++i) { - if (values[i] == values[i+1]) { - set_false(e); - return; - } - } - set_true(e); - break; - } - default: - IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); - UNREACHABLE(); - } - } - - bool model_evaluator::check_model(ptr_vector const& formulas) { - ptr_vector todo(formulas); - - while (!todo.empty()) { - expr * curr_e = todo.back(); - - if (!is_app(curr_e)) { - todo.pop_back(); - continue; - } - app * curr = to_app(curr_e); - - if (!is_unknown(curr)) { - todo.pop_back(); - continue; - } - unsigned arity = curr->get_num_args(); - for (unsigned i = 0; i < arity; ++i) { - if (is_unknown(curr->get_arg(i))) { - todo.push_back(curr->get_arg(i)); - } - } - if (todo.back() != curr) { - continue; - } - todo.pop_back(); - if (curr->get_family_id() == m_arith.get_family_id()) { - eval_arith(curr); - } - else if (curr->get_family_id() == m.get_basic_family_id()) { - eval_basic(curr); - } - else { - expr_ref vl(m); - m_model->eval(curr, vl); - assign_value(curr, vl); - } - - IF_VERBOSE(35,verbose_stream() << "assigned "<get_arity() == 0); - expr_ref result(m); - if (m_array.is_array(d->get_range())) { - expr_ref e(m); - e = m.mk_const(d); - result = eval(model, e); - } - else { - result = model->get_const_interp(d); - } - return result; - } - - expr_ref model_evaluator::eval(model_ref& model, expr* e) { - expr_ref result(m); - m_model = model; - VERIFY(m_model->eval(e, result, true)); - if (m_array.is_array(e)) { - vector stores; - expr_ref_vector args(m); - expr_ref else_case(m); - if (extract_array_func_interp(result, stores, else_case)) { - result = m_array.mk_const_array(m.get_sort(e), else_case); - while (!stores.empty() && stores.back().back() == else_case) { - stores.pop_back(); - } - for (unsigned i = stores.size(); i > 0; ) { - --i; - args.resize(1); - args[0] = result; - args.append(stores[i]); - result = m_array.mk_store(args.size(), args.c_ptr()); - } - return result; - } - } - return result; - } - - void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); - qe::flatten_and(fml, conjs); + flatten_and(fml, conjs); obj_map diseqs; expr* n, *lhs, *rhs; for (unsigned i = 0; i < conjs.size(); ++i) { @@ -1030,68 +157,6 @@ namespace pdr { fml = m.mk_and(conjs.size(), conjs.c_ptr()); } - // - // (f (if c1 (if c2 e1 e2) e3) b c) -> - // (if c1 (if c2 (f e1 b c) - - class ite_hoister { - ast_manager& m; - public: - ite_hoister(ast_manager& m): m(m) {} - - br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { - if (m.is_ite(f)) { - return BR_FAILED; - } - for (unsigned i = 0; i < num_args; ++i) { - expr* c, *t, *e; - if (!m.is_bool(args[i]) && m.is_ite(args[i], c, t, e)) { - expr_ref e1(m), e2(m); - ptr_vector args1(num_args, args); - args1[i] = t; - e1 = m.mk_app(f, num_args, args1.c_ptr()); - if (t == e) { - result = e1; - return BR_REWRITE1; - } - args1[i] = e; - e2 = m.mk_app(f, num_args, args1.c_ptr()); - result = m.mk_app(f, num_args, args); - result = m.mk_ite(c, e1, e2); - return BR_REWRITE3; - } - } - return BR_FAILED; - } - }; - - struct ite_hoister_cfg: public default_rewriter_cfg { - ite_hoister m_r; - bool rewrite_patterns() const { return false; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - return m_r.mk_app_core(f, num, args, result); - } - ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} - }; - - class ite_hoister_star : public rewriter_tpl { - ite_hoister_cfg m_cfg; - public: - ite_hoister_star(ast_manager & m, params_ref const & p): - rewriter_tpl(m, false, m_cfg), - m_cfg(m, p) {} - }; - - void hoist_non_bool_if(expr_ref& fml) { - ast_manager& m = fml.get_manager(); - scoped_no_proof _sp(m); - params_ref p; - ite_hoister_star ite_rw(m, p); - expr_ref tmp(m); - ite_rw(fml, tmp); - fml = tmp; - } - class test_diff_logic { ast_manager& m; arith_util a; @@ -1441,7 +506,6 @@ namespace pdr { } -template class rewriter_tpl; template class rewriter_tpl; diff --git a/src/muz/pdr/pdr_util.h b/src/muz/pdr/pdr_util.h index 446bde8aa..09fad8a30 100644 --- a/src/muz/pdr/pdr_util.h +++ b/src/muz/pdr/pdr_util.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _PDR_UTIL_H_ -#define _PDR_UTIL_H_ +#ifndef PDR_UTIL_H_ +#define PDR_UTIL_H_ #include "ast.h" #include "ast_pp.h" @@ -54,86 +54,6 @@ namespace pdr { std::string pp_cube(unsigned sz, app * const * lits, ast_manager& manager); std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& manager); - class model_evaluator { - ast_manager& m; - arith_util m_arith; - array_util m_array; - obj_map m_numbers; - expr_ref_vector m_refs; - obj_map m_values; - model_ref m_model; - - //00 -- non-visited - //01 -- X - //10 -- false - //11 -- true - expr_mark m1; - expr_mark m2; - expr_mark m_visited; - - - void reset(); - void setup_model(model_ref& model); - void assign_value(expr* e, expr* v); - void collect(ptr_vector const& formulas, ptr_vector& tocollect); - void process_formula(app* e, ptr_vector& todo, ptr_vector& tocollect); - expr_ref_vector prune_by_cone_of_influence(ptr_vector const & formulas); - void eval_arith(app* e); - void eval_basic(app* e); - void eval_eq(app* e, expr* arg1, expr* arg2); - void eval_array_eq(app* e, expr* arg1, expr* arg2); - void inherit_value(expr* e, expr* v); - - inline bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); } - inline void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); } - inline bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); } - inline bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); } - inline bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); } - inline void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); } - inline void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } - inline void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } - inline void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); } - inline void set_bool(expr* x, bool v) { if (v) { set_true(x); } else { set_false(x); } } - inline rational const& get_number(expr* x) const { return m_numbers.find(x); } - inline void set_number(expr* x, rational const& v) { - set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_numbers.insert(x,v); - } - inline expr* get_value(expr* x) { return m_values.find(x); } - inline void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); } - - bool check_model(ptr_vector const & formulas); - - bool extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case); - - void eval_exprs(expr_ref_vector& es); - - public: - model_evaluator(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {} - - /** - \brief extract equalities from model that suffice to satisfy formula. - - \pre model satisfies formulas - */ - - expr_ref_vector minimize_model(ptr_vector const & formulas, model_ref& mdl); - - /** - \brief extract literals from formulas that satisfy formulas. - - \pre model satisfies formulas - */ - expr_ref_vector minimize_literals(ptr_vector const & formulas, model_ref& mdl); - - /** - for_each_expr visitor. - */ - void operator()(expr* e) {} - - expr_ref eval(model_ref& mdl, expr* e); - - expr_ref eval(model_ref& mdl, func_decl* d); - }; /** \brief replace variables that are used in many disequalities by @@ -143,12 +63,6 @@ namespace pdr { */ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); - /** - \brief hoist non-boolean if expressions. - */ - void hoist_non_bool_if(expr_ref& fml); - - /** \brief normalize coefficients in polynomials so that least coefficient is 1. */ diff --git a/src/muz/rel/aig_exporter.h b/src/muz/rel/aig_exporter.h index 78ab9fe17..71af6fb35 100644 --- a/src/muz/rel/aig_exporter.h +++ b/src/muz/rel/aig_exporter.h @@ -11,8 +11,8 @@ Abstract: --*/ -#ifndef _AIG_EXPORTER_H_ -#define _AIG_EXPORTER_H_ +#ifndef AIG_EXPORTER_H_ +#define AIG_EXPORTER_H_ #include "aig.h" #include "dl_rule_set.h" diff --git a/src/muz/rel/check_relation.cpp b/src/muz/rel/check_relation.cpp new file mode 100644 index 000000000..7c6dac5e4 --- /dev/null +++ b/src/muz/rel/check_relation.cpp @@ -0,0 +1,789 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "check_relation.h" +#include "dl_relation_manager.h" +#include "qe_util.h" +#include "ast_util.h" +#include "smt_kernel.h" +#include + + +namespace datalog { + + check_relation::check_relation(check_relation_plugin& p, relation_signature const& sig, relation_base* r): + relation_base(p, sig), + m(p.get_ast_manager()), + m_relation(r), + m_fml(m) { + m_relation->to_formula(m_fml); + } + check_relation::~check_relation() { + m_relation->deallocate(); + } + void check_relation::check_equiv(char const* objective, expr* f1, expr* f2) const { + get_plugin().check_equiv(objective, f1, f2); + } + void check_relation::consistent_formula() { + expr_ref fml(m); + m_relation->to_formula(fml); + if (m_fml != fml) { + IF_VERBOSE(0, display(verbose_stream() << "relation does not have a consistent formula");); + } + } + expr_ref check_relation::mk_eq(relation_fact const& f) const { + relation_signature const& sig = get_signature(); + expr_ref_vector conjs(m); + for (unsigned i = 0; i < sig.size(); ++i) { + conjs.push_back(m.mk_eq(m.mk_var(i, sig[i]), f[i])); + } + return expr_ref(mk_and(m, conjs.size(), conjs.c_ptr()), m); + } + + expr_ref check_relation::ground(expr* fml) const { + return get_plugin().ground(*this, fml); + } + + expr_ref check_relation_plugin::ground(relation_base const& dst) const { + expr_ref fml(m); + dst.to_formula(fml); + return ground(dst, fml); + } + + expr_ref check_relation_plugin::ground(relation_base const& dst, expr* fml) const { + relation_signature const& sig = dst.get_signature(); + var_subst sub(m, false); + expr_ref_vector vars(m); + for (unsigned i = 0; i < sig.size(); ++i) { + vars.push_back(m.mk_const(symbol(i), sig[i])); + } + expr_ref result(m); + sub(fml, vars.size(), vars.c_ptr(), result); + return result; + } + + void check_relation::add_fact(const relation_fact & f) { + expr_ref fml1(m); + m_relation->add_fact(f); + m_relation->to_formula(fml1); + m_fml = m.mk_or(m_fml, mk_eq(f)); + check_equiv("add_fact", ground(m_fml), ground(fml1)); + m_fml = fml1; + } + void check_relation::add_new_fact(const relation_fact & f) { + expr_ref fml1(m); + m_relation->add_new_fact(f); + m_relation->to_formula(fml1); + m_fml = m.mk_or(m_fml, mk_eq(f)); + check_equiv("add_fact", ground(m_fml), ground(fml1)); + m_fml = fml1; + } + bool check_relation::empty() const { + bool result = m_relation->empty(); + if (result && !m.is_false(m_fml)) { + check_equiv("empty", m.mk_false(), ground(m_fml)); + } + return result; + } + bool check_relation::fast_empty() const { + bool result = m_relation->fast_empty(); + if (result && !m.is_false(m_fml)) { + check_equiv("fast_empty", m.mk_false(), ground(m_fml)); + } + return result; + } + void check_relation::reset() { + m_relation->reset(); + m_fml = m.mk_false(); + } + + bool check_relation::contains_fact(const relation_fact & f) const { + bool result = m_relation->contains_fact(f); + expr_ref fml1(m), fml2(m); + fml1 = mk_eq(f); + fml2 = m.mk_and(m_fml, fml1); + if (result) { + check_equiv("contains fact", ground(fml1), ground(fml2)); + } + else if (!m.is_false(m_fml)) { + check_equiv("contains fact", ground(fml2), m.mk_false()); + } + return result; + } + check_relation * check_relation::clone() const { + check_relation* result = check_relation_plugin::get(get_plugin().mk_empty(get_signature())); + result->m_relation->deallocate(); + result->m_relation = m_relation->clone(); + result->m_relation->to_formula(result->m_fml); + if (m_fml != result->m_fml) { + check_equiv("clone", ground(m_fml), ground(result->m_fml)); + } + return result; + } + check_relation * check_relation::complement(func_decl* f) const { + check_relation* result = check_relation_plugin::get(get_plugin().mk_empty(get_signature())); + result->m_relation->deallocate(); + result->m_relation = m_relation->complement(f); + result->m_relation->to_formula(result->m_fml); + expr_ref fml(m); + fml = m.mk_not(m_fml); + check_equiv("complement", ground(fml), ground(result->m_fml)); + return result; + } + void check_relation::to_formula(expr_ref& fml) const { + fml = m_fml; + } + + check_relation_plugin& check_relation::get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + void check_relation::display(std::ostream& out) const { + m_relation->display(out); + out << m_fml << "\n"; + } + + // ------------- + + check_relation_plugin::check_relation_plugin(relation_manager& rm): + relation_plugin(check_relation_plugin::get_name(), rm), + m(rm.get_context().get_manager()), + m_base(0) { + } + check_relation_plugin::~check_relation_plugin() { + } + check_relation& check_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + check_relation* check_relation_plugin::get(relation_base* r) { + return r?dynamic_cast(r):0; + } + check_relation const & check_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + bool check_relation_plugin::can_handle_signature(const relation_signature & sig) { + return m_base && m_base->can_handle_signature(sig); + } + relation_base * check_relation_plugin::mk_empty(const relation_signature & sig) { + relation_base* r = m_base->mk_empty(sig); + check_relation* result = alloc(check_relation, *this, sig, r); + if (result->m_fml != m.mk_false()) { + check_equiv("mk_empty", result->ground(result->m_fml), m.mk_false()); + } + return result; + } + relation_base * check_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + relation_base* r = m_base->mk_full(p, s); + check_relation* result = alloc(check_relation, *this, s, r); + if (result->m_fml != m.mk_true()) { + check_equiv("mk_full", result->ground(result->m_fml), m.mk_true()); + } + return result; + } + + class check_relation_plugin::join_fn : public convenient_relation_join_fn { + scoped_ptr m_join; + public: + join_fn(relation_join_fn* j, + const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), m_join(j) + {} + virtual ~join_fn() {} + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + check_relation const& t1 = get(r1); + check_relation const& t2 = get(r2); + check_relation_plugin& p = t1.get_plugin(); + relation_base* r = (*m_join)(t1.rb(), t2.rb()); + p.verify_join(r1, r2, *r, m_cols1, m_cols2); + return alloc(check_relation, p, r->get_signature(), r); + } + }; + + relation_join_fn * check_relation_plugin::mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + relation_join_fn* j = m_base->mk_join_fn(get(t1).rb(), get(t2).rb(), col_cnt, cols1, cols2); + return j?alloc(join_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2):0; + } + + class check_relation_plugin::join_project_fn : public convenient_relation_join_project_fn { + scoped_ptr m_join; + public: + join_project_fn( + relation_join_fn* j, + const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned* removed_cols) + : convenient_join_project_fn(o1_sig, o2_sig, col_cnt, cols1, cols2, + removed_col_cnt, removed_cols), m_join(j) + {} + virtual ~join_project_fn() {} + virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + check_relation const& t1 = get(r1); + check_relation const& t2 = get(r2); + check_relation_plugin& p = t1.get_plugin(); + relation_base* r = (*m_join)(t1.rb(), t2.rb()); + p.verify_join_project(r1, r2, *r, m_cols1, m_cols2, m_removed_cols); + return alloc(check_relation, p, r->get_signature(), r); + } + }; + + relation_join_fn * check_relation_plugin::mk_join_project_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + relation_join_fn* j = m_base->mk_join_project_fn(get(t1).rb(), get(t2).rb(), col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + return j?alloc(join_project_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, + removed_col_cnt, removed_cols):0; + } + + void check_relation_plugin::verify_filter_project( + relation_base const& src, relation_base const& dst, + app* cond, unsigned_vector const& removed_cols) { + expr_ref fml1(m), fml2(m); + src.to_formula(fml1); + dst.to_formula(fml2); + fml1 = m.mk_and(cond, fml1); + verify_project(src, fml1, dst, fml2, removed_cols); + } + + void check_relation_plugin::verify_project( + relation_base const& src, + relation_base const& dst, + unsigned_vector const& removed_cols) { + expr_ref fml1(m), fml2(m); + src.to_formula(fml1); + dst.to_formula(fml2); + verify_project(src, fml1, dst, fml2, removed_cols); + } + void check_relation_plugin::verify_project( + relation_base const& src, expr* f1, + relation_base const& dst, expr* f2, + unsigned_vector const& removed_cols) { + expr_ref fml1 = ground(dst, mk_project(src.get_signature(), f1, removed_cols)); + expr_ref fml2 = ground(dst, f2); + check_equiv("project", fml1, fml2); + } + expr_ref check_relation_plugin::mk_project( + relation_signature const& sig, + expr* fml, unsigned_vector const& removed_cols) { + expr_ref fml1(m); + ptr_vector bound; + svector names; + expr_ref_vector vars(m); + unsigned rm_cnt = removed_cols.size(); + for (unsigned i = 0, j = 0, k = 0; i < sig.size(); ++i) { + if (j < rm_cnt && removed_cols[j] == i) { + std::ostringstream strm; + strm << "x" << j; + bound.push_back(sig[i]); + names.push_back(symbol(strm.str().c_str())); + vars.push_back(m.mk_var(j, sig[i])); + ++j; + } + else { + vars.push_back(m.mk_var(k + rm_cnt, sig[i])); + ++k; + } + } + var_subst sub(m, false); + sub(fml, vars.size(), vars.c_ptr(), fml1); + bound.reverse(); + fml1 = m.mk_exists(bound.size(), bound.c_ptr(), names.c_ptr(), fml1); + return fml1; + } + + void check_relation_plugin::verify_join_project( + relation_base const& t1, relation_base const& t2, relation_base const& t, + unsigned_vector const& cols1, unsigned_vector const& cols2, unsigned_vector const& rm_cols) { + ast_manager& m = get_ast_manager(); + relation_signature const& sigA = t1.get_signature(); + relation_signature const& sigB = t2.get_signature(); + relation_signature sig1; + sig1.append(sigA); + sig1.append(sigB); + + expr_ref fml1 = mk_join(t1, t2, cols1, cols2); + fml1 = mk_project(sig1, fml1, rm_cols); + fml1 = ground(t, fml1); + expr_ref fml2(m); + t.to_formula(fml2); + fml2 = ground(t, fml2); + check_equiv("join_project", fml1, fml2); + } + + expr_ref check_relation_plugin::mk_join( + relation_base const& t1, relation_base const& t2, + unsigned_vector const& cols1, unsigned_vector const& cols2) { + ast_manager& m = get_ast_manager(); + expr_ref fml1(m), fml2(m), fml3(m); + + relation_signature const& sig1 = t1.get_signature(); + relation_signature const& sig2 = t2.get_signature(); + var_ref var1(m), var2(m); + t1.to_formula(fml1); + t2.to_formula(fml2); + var_subst sub(m, false); + expr_ref_vector vars(m); + for (unsigned i = 0; i < sig2.size(); ++i) { + vars.push_back(m.mk_var(i + sig1.size(), sig2[i])); + } + sub(fml2, vars.size(), vars.c_ptr(), fml2); + fml1 = m.mk_and(fml1, fml2); + for (unsigned i = 0; i < cols1.size(); ++i) { + unsigned v1 = cols1[i]; + unsigned v2 = cols2[i]; + var1 = m.mk_var(v1, sig1[v1]); + var2 = m.mk_var(v2 + sig1.size(), sig2[v2]); + fml1 = m.mk_and(m.mk_eq(var1, var2), fml1); + } + return fml1; + } + + + void check_relation_plugin::verify_permutation( + relation_base const& src, relation_base const& dst, + unsigned_vector const& cycle) { + unsigned_vector perm; + relation_signature const& sig1 = src.get_signature(); + relation_signature const& sig2 = dst.get_signature(); + for (unsigned i = 0; i < sig1.size(); ++i) { + perm.push_back(i); + } + for (unsigned i = 0; i < cycle.size(); ++i) { + unsigned j = (i + 1)%cycle.size(); + unsigned col1 = cycle[i]; + unsigned col2 = cycle[j]; + perm[col2] = col1; + } + for (unsigned i = 0; i < perm.size(); ++i) { + SASSERT(sig2[perm[i]] == sig1[i]); + } + expr_ref_vector sub(m); + for (unsigned i = 0; i < perm.size(); ++i) { + sub.push_back(m.mk_var(perm[i], sig1[i])); + } + var_subst subst(m, false); + expr_ref fml1(m), fml2(m); + src.to_formula(fml1); + dst.to_formula(fml2); + subst(fml1, sub.size(), sub.c_ptr(), fml1); + expr_ref_vector vars(m); + for (unsigned i = 0; i < sig2.size(); ++i) { + vars.push_back(m.mk_const(symbol(i), sig2[i])); + } + + subst(fml1, vars.size(), vars.c_ptr(), fml1); + subst(fml2, vars.size(), vars.c_ptr(), fml2); + + check_equiv("permutation", fml1, fml2); + } + + void check_relation_plugin::verify_join( + relation_base const& t1, relation_base const& t2, relation_base const& t, + unsigned_vector const& cols1, unsigned_vector const& cols2) { + expr_ref fml1 = ground(t, mk_join(t1, t2, cols1, cols2)); + expr_ref fml2 = ground(t); + check_equiv("join", fml1, fml2); + } + + void check_relation_plugin::verify_filter(expr* fml0, relation_base const& t, expr* cond) { + expr_ref fml1(m), fml2(m); + fml1 = m.mk_and(fml0, cond); + t.to_formula(fml2); + + relation_signature const& sig = t.get_signature(); + expr_ref_vector vars(m); + var_subst sub(m, false); + for (unsigned i = 0; i < sig.size(); ++i) { + std::stringstream strm; + strm << "x" << i; + vars.push_back(m.mk_const(symbol(strm.str().c_str()), sig[i])); + } + sub(fml1, vars.size(), vars.c_ptr(), fml1); + sub(fml2, vars.size(), vars.c_ptr(), fml2); + check_equiv("filter", fml1, fml2); + } + + void check_relation_plugin::check_contains(char const* objective, expr* fml1, expr* fml2) { + expr_ref fml0(m); + fml0 = m.mk_and(fml1, fml2); + check_equiv(objective, fml0, fml2); + } + + void check_relation_plugin::check_equiv(char const* objective, expr* fml1, expr* fml2) { + TRACE("doc", tout << mk_pp(fml1, m) << "\n"; + tout << mk_pp(fml2, m) << "\n";); + smt_params fp; + smt::kernel solver(m, fp); + expr_ref tmp(m); + tmp = m.mk_not(m.mk_eq(fml1, fml2)); + solver.assert_expr(tmp); + lbool res = solver.check(); + if (res == l_false) { + IF_VERBOSE(3, verbose_stream() << objective << " verified\n";); + } + else if (res == l_true) { + IF_VERBOSE(0, verbose_stream() << "NOT verified " << res << "\n"; + verbose_stream() << mk_pp(fml1, m) << "\n"; + verbose_stream() << mk_pp(fml2, m) << "\n"; + verbose_stream().flush(); + ); + throw default_exception("operation was not verified"); + } + } + + void check_relation_plugin::verify_union(expr* dst0, relation_base const& src, + relation_base const& dst, + expr* delta0, relation_base const* delta) { + expr_ref fml1(m), fml2(m); + src.to_formula(fml1); + dst.to_formula(fml2); + fml1 = m.mk_or(fml1, dst0); + relation_signature const& sig = dst.get_signature(); + expr_ref_vector vars(m); + var_subst sub(m, false); + for (unsigned i = 0; i < sig.size(); ++i) { + std::stringstream strm; + strm << "x" << i; + vars.push_back(m.mk_const(symbol(strm.str().c_str()), sig[i])); + } + sub(fml1, vars.size(), vars.c_ptr(), fml1); + sub(fml2, vars.size(), vars.c_ptr(), fml2); + + check_equiv("union", fml1, fml2); + + if (delta) { + expr_ref d0(m), d(m); + delta->to_formula(d); + IF_VERBOSE(3, verbose_stream() << "verify delta " << d << "\n";); + // delta >= dst \ dst0 + // dst \ dst0 == delta & dst & \ dst0 + expr_ref fml4(m), fml5(m); + fml4 = m.mk_and(fml2, m.mk_not(dst0)); + sub(fml4, vars.size(), vars.c_ptr(), fml4); + sub(d, vars.size(), vars.c_ptr(), d); + check_contains("union_delta low", d, fml4); + // + // delta >= delta0 + // + sub(delta0, vars.size(), vars.c_ptr(), d0); + check_contains("union delta0", d, d0); + + // + // dst u delta0 = delta u dst0 + // + fml4 = m.mk_or(fml2, delta0); + fml5 = m.mk_or(d, dst0); + sub(fml4, vars.size(), vars.c_ptr(), fml4); + sub(fml5, vars.size(), vars.c_ptr(), fml5); + check_equiv("union no overflow", fml4, fml5); + } + } + + class check_relation_plugin::union_fn : public relation_union_fn { + scoped_ptr m_union; + public: + union_fn(relation_union_fn* m): m_union(m) {} + + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + check_relation& r = get(_r); + check_relation const& src = get(_src); + check_relation* d = get(_delta); + expr_ref fml0 = r.m_fml; + expr_ref delta0(r.m_fml.get_manager()); + if (d) d->to_formula(delta0); + (*m_union)(r.rb(), src.rb(), d?(&d->rb()):0); + r.get_plugin().verify_union(fml0, src.rb(), r.rb(), delta0, d?(&d->rb()):0); + r.rb().to_formula(r.m_fml); + if (d) d->rb().to_formula(d->m_fml); + } + }; + relation_union_fn * check_relation_plugin::mk_union_fn( + const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + relation_base const* d1 = delta?(&(get(*delta).rb())):0; + relation_union_fn* u = m_base->mk_union_fn(get(tgt).rb(), get(src).rb(), d1); + return u?alloc(union_fn, u):0; + } + relation_union_fn * check_relation_plugin::mk_widen_fn( + const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + relation_base const* d1 = delta?(&(get(*delta).rb())):0; + relation_union_fn* u = m_base->mk_widen_fn(get(tgt).rb(), get(src).rb(), d1); + return u?alloc(union_fn, u):0; + } + + class check_relation_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_cols; + scoped_ptr m_filter; + public: + filter_identical_fn(relation_mutator_fn* f, unsigned col_cnt, const unsigned *identical_cols) + : m_cols(col_cnt, identical_cols), + m_filter(f) { + } + + virtual ~filter_identical_fn() {} + + virtual void operator()(relation_base & _r) { + check_relation& r = get(_r); + check_relation_plugin& p = r.get_plugin(); + ast_manager& m = p.m; + expr_ref cond(m); + relation_signature const& sig = r.get_signature(); + expr_ref_vector conds(m); + unsigned c1 = m_cols[0]; + for (unsigned i = 1; i < m_cols.size(); ++i) { + unsigned c2 = m_cols[i]; + conds.push_back(m.mk_eq(m.mk_var(c1, sig[c1]), m.mk_var(c2, sig[c2]))); + } + cond = mk_and(m, conds.size(), conds.c_ptr()); + r.consistent_formula(); + (*m_filter)(r.rb()); + p.verify_filter(r.m_fml, r.rb(), cond); + r.rb().to_formula(r.m_fml); + } + }; + relation_mutator_fn * check_relation_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + relation_mutator_fn* r = m_base->mk_filter_identical_fn(get(t).rb(), col_cnt, identical_cols); + return r?alloc(filter_identical_fn, r, col_cnt, identical_cols):0; + } + + class check_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + scoped_ptr m_mutator; + app_ref m_condition; + public: + filter_interpreted_fn(relation_mutator_fn* r, app_ref& condition) : + m_mutator(r), + m_condition(condition) { + } + + virtual ~filter_interpreted_fn() {} + + virtual void operator()(relation_base & tb) { + check_relation& r = get(tb); + check_relation_plugin& p = r.get_plugin(); + expr_ref fml = r.m_fml; + (*m_mutator)(r.rb()); + p.verify_filter(fml, r.rb(), m_condition); + r.rb().to_formula(r.m_fml); + } + }; + relation_mutator_fn * check_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + relation_mutator_fn* r = m_base->mk_filter_interpreted_fn(get(t).rb(), condition); + app_ref cond(condition, m); + return r?alloc(filter_interpreted_fn, r, cond):0; + } + + class check_relation_plugin::project_fn : public convenient_relation_project_fn { + scoped_ptr m_project; + public: + project_fn(relation_transformer_fn* p, + relation_base const & t, + unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(t.get_signature(), removed_col_cnt, removed_cols), + m_project(p) { + } + + virtual ~project_fn() {} + + virtual relation_base * operator()(const relation_base & tb) { + check_relation const& t = get(tb); + check_relation_plugin& p = t.get_plugin(); + relation_base* r = (*m_project)(t.rb()); + p.verify_project(tb, *r, m_removed_cols); + return alloc(check_relation, p, r->get_signature(), r); + } + }; + + + relation_transformer_fn * check_relation_plugin::mk_project_fn( + const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + relation_transformer_fn* p = m_base->mk_project_fn(get(t).rb(), col_cnt, removed_cols); + return p?alloc(project_fn, p, t, col_cnt, removed_cols):0; + } + + class check_relation_plugin::rename_fn : public convenient_relation_rename_fn { + scoped_ptr m_permute; + public: + rename_fn(relation_transformer_fn* permute, + relation_base const& t, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(t.get_signature(), cycle_len, cycle), + m_permute(permute) { + } + + virtual ~rename_fn() {} + + virtual relation_base * operator()(const relation_base & _t) { + check_relation const& t = get(_t); + check_relation_plugin& p = t.get_plugin(); + relation_signature const& sig = get_result_signature(); + relation_base* r = (*m_permute)(t.rb()); + p.verify_permutation(t.rb(), *r, m_cycle); + return alloc(check_relation, p, sig, r); + } + }; + relation_transformer_fn * check_relation_plugin::mk_rename_fn( + const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + relation_transformer_fn* p = m_base->mk_rename_fn(get(r).rb(), cycle_len, permutation_cycle); + return p?alloc(rename_fn, p, r, cycle_len, permutation_cycle):0; + } + + class check_relation_plugin::filter_equal_fn : public relation_mutator_fn { + scoped_ptr m_filter; + relation_element m_val; + unsigned m_col; + public: + filter_equal_fn(relation_mutator_fn* filter, relation_base const& t, + const relation_element val, unsigned col): + m_filter(filter), + m_val(val), + m_col(col) + {} + virtual ~filter_equal_fn() { } + virtual void operator()(relation_base & tb) { + check_relation & t = get(tb); + check_relation_plugin& p = t.get_plugin(); + (*m_filter)(t.rb()); + expr_ref fml = t.m_fml; + t.rb().to_formula(t.m_fml); + fml = p.m.mk_and(fml, p.m.mk_eq(p.m.mk_var(m_col, t.get_signature()[m_col]), m_val)); + p.check_equiv("filter_equal", t.ground(fml), t.ground(t.m_fml)); + } + }; + relation_mutator_fn * check_relation_plugin::mk_filter_equal_fn( + const relation_base & t, const relation_element & value, unsigned col) { + relation_mutator_fn* r = m_base->mk_filter_equal_fn(get(t).rb(), value, col); + return r?alloc(filter_equal_fn, r, t, value, col):0; + } + + + + + class check_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { + scoped_ptr m_filter; + const unsigned_vector m_t_cols; + const unsigned_vector m_neg_cols; + public: + negation_filter_fn( + relation_intersection_filter_fn* filter, + unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *neg_cols) + : m_filter(filter), + m_t_cols(joined_col_cnt, t_cols), m_neg_cols(joined_col_cnt, neg_cols) { + SASSERT(joined_col_cnt > 0); + } + + virtual void operator()(relation_base& tb, const relation_base& negb) { + check_relation& t = get(tb); + check_relation const& n = get(negb); + check_relation_plugin& p = t.get_plugin(); + ast_manager& m = p.get_ast_manager(); + expr_ref dst0(m); + t.to_formula(dst0); + (*m_filter)(t.rb(), n.rb()); + t.rb().to_formula(t.m_fml); + p.verify_filter_by_negation(dst0, t.rb(), n.rb(), m_t_cols, m_neg_cols); + } + }; + + relation_intersection_filter_fn * check_relation_plugin::mk_filter_by_negation_fn( + const relation_base& t, + const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, + const unsigned *negated_cols) { + relation_intersection_filter_fn* f = m_base->mk_filter_by_negation_fn(get(t).rb(), get(neg).rb(), joined_col_cnt, t_cols, negated_cols); + return f?alloc(negation_filter_fn, f, joined_col_cnt, t_cols, negated_cols):0; + } + + /* + The filter_by_negation postcondition: + filter_by_negation(tgt, neg, columns in tgt: c1,...,cN, + corresponding columns in neg: d1,...,dN): + tgt_1:={x: x\in tgt_0 && ! \exists y: ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } + */ + + void check_relation_plugin::verify_filter_by_negation( + expr* dst0, + relation_base const& dst, + relation_base const& neg, + unsigned_vector const& cols1, + unsigned_vector const& cols2) { + relation_signature const& sig1 = dst.get_signature(); + relation_signature const& sig2 = neg.get_signature(); + expr_ref dstf(m), negf(m); + //std::cout << mk_pp(dst0, m) << "\n"; + expr_ref_vector eqs(m); + dst.to_formula(dstf); + //std::cout << mk_pp(dstf, m) << "\n"; + neg.to_formula(negf); + //std::cout << mk_pp(negf, m) << "\n"; + eqs.push_back(negf); + for (unsigned i = 0; i < cols1.size(); ++i) { + var_ref v1(m), v2(m); + unsigned c1 = cols1[i]; + unsigned c2 = cols2[i]; + SASSERT(sig1[c1] == sig2[c2]); + v1 = m.mk_var(sig2.size() + c1, sig1[c1]); + v2 = m.mk_var(c2, sig2[c2]); + eqs.push_back(m.mk_eq(v1, v2)); + } + negf = mk_and(m, eqs.size(), eqs.c_ptr()); + ptr_vector rev_sig2(sig2.size(), sig2.c_ptr()); + rev_sig2.reverse(); + svector names; + for (unsigned i = 0; i < sig2.size(); ++i) { + names.push_back(symbol(i)); + } + negf = m.mk_exists(rev_sig2.size(), rev_sig2.c_ptr(), names.c_ptr(), negf); + negf = m.mk_and(dst0, m.mk_not(negf)); + negf = ground(dst, negf); + dstf = ground(dst, dstf); + //std::cout << negf << "\n"; + //std::cout << dstf << "\n"; + check_equiv("filter by negation", dstf, negf); + } + + class check_relation_plugin::filter_proj_fn : public convenient_relation_project_fn { + app_ref m_cond; + scoped_ptr m_xform; + public: + filter_proj_fn(relation_transformer_fn* xform, + relation_base const& t, app_ref& cond, + unsigned col_cnt, const unsigned * removed_cols) : + convenient_relation_project_fn(t.get_signature(), col_cnt, removed_cols), + m_cond(cond), + m_xform(xform) + {} + + virtual ~filter_proj_fn() {} + + virtual relation_base* operator()(const relation_base & tb) { + check_relation const & t = get(tb); + check_relation_plugin& p = t.get_plugin(); + relation_base* r = (*m_xform)(t.rb()); + p.verify_filter_project(t.rb(), *r, m_cond, m_removed_cols); + relation_signature const& sig = get_result_signature(); + return alloc(check_relation, p, sig, r); + } + }; + relation_transformer_fn * check_relation_plugin::mk_filter_interpreted_and_project_fn( + const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) { + relation_transformer_fn* r = m_base->mk_filter_interpreted_and_project_fn(get(t).rb(), condition, removed_col_cnt, removed_cols); + app_ref cond(condition, m); + return r?alloc(filter_proj_fn, r, t, cond, removed_col_cnt, removed_cols):0; + } + + + + +} diff --git a/src/muz/rel/check_relation.h b/src/muz/rel/check_relation.h new file mode 100644 index 000000000..c91567553 --- /dev/null +++ b/src/muz/rel/check_relation.h @@ -0,0 +1,172 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + check_relation.h + +Abstract: + + Checked relation. + Each operation on an underlying relation is checked for correctness. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-09-23 + +Revision History: + + +--*/ + +#ifndef CHECK_RELATION_H_ +#define CHECK_RELATION_H_ + +#include "doc.h" +#include "dl_base.h" + +namespace datalog { + class check_relation_plugin; + class check_relation; + + class check_relation : public relation_base { + friend class check_relation_plugin; + ast_manager& m; + relation_base* m_relation; + expr_ref m_fml; + void check_equiv(char const* objective, expr* f1, expr* f2) const; + expr_ref mk_eq(relation_fact const& f) const; + public: + check_relation(check_relation_plugin& p, relation_signature const& s, relation_base* r); + virtual ~check_relation(); + virtual void reset(); + virtual void add_fact(const relation_fact & f); + virtual void add_new_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual check_relation * clone() const; + virtual check_relation * complement(func_decl*) const; + virtual void to_formula(expr_ref& fml) const; + check_relation_plugin& get_plugin() const; + virtual bool fast_empty() const; + virtual bool empty() const; + virtual void display(std::ostream& out) const; + virtual bool is_precise() const { return m_relation->is_precise(); } + virtual unsigned get_size_estimate_rows() const { return m_relation->get_size_estimate_rows(); } + relation_base& rb() { return *m_relation; } + relation_base const& rb() const { return *m_relation; } + expr_ref ground(expr* fml) const; + void consistent_formula(); + }; + + class check_relation_plugin : public relation_plugin { + friend class check_relation; + + class join_fn; + class join_project_fn; + class project_fn; + class union_fn; + class rename_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class filter_by_negation_fn; + class filter_by_union_fn; + class filter_proj_fn; + class negation_filter_fn; + ast_manager& m; + relation_plugin* m_base; + static check_relation& get(relation_base& r); + static check_relation* get(relation_base* r); + static check_relation const & get(relation_base const& r); + expr_ref ground(relation_base const& rb, expr* fml) const; + expr_ref ground(relation_base const& rb) const; + + expr_ref mk_project( + relation_signature const& sig, + expr* fml, unsigned_vector const& removed_cols); + + expr_ref mk_join( + relation_base const& t1, relation_base const& t2, + unsigned_vector const& cols1, unsigned_vector const& cols2); + public: + check_relation_plugin(relation_manager& rm); + ~check_relation_plugin(); + void set_plugin(relation_plugin* p) { m_base = p; } + + virtual bool can_handle_signature(const relation_signature & s); + static symbol get_name() { return symbol("check_relation"); } + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_join_fn * mk_join_project_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn( + const relation_base& t, + const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, + const unsigned *negated_cols); + virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn( + const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + + void verify_join(relation_base const& t1, relation_base const& t2, relation_base const& t, + unsigned_vector const& cols1, unsigned_vector const& cols2); + + + void verify_filter(expr* fml0, relation_base const& t, expr* cond); + + void verify_union(expr* fml0, relation_base const& src, relation_base const& dst, + expr* delta0, relation_base const* delta); + + void verify_permutation( + relation_base const& src, relation_base const& dst, + unsigned_vector const& cycle); + + void verify_project( + relation_base const& src, expr* f1, + relation_base const& dst, expr* f2, + unsigned_vector const& removed_cols); + + void verify_project( + relation_base const& src, + relation_base const& dst, + unsigned_vector const& removed_cols); + + void verify_filter_project( + relation_base const& src, relation_base const& dst, + app* cond, unsigned_vector const& removed_cols); + + void verify_join_project( + relation_base const& t1, relation_base const& t2, relation_base const& t, + unsigned_vector const& cols1, unsigned_vector const& cols2, unsigned_vector const& rm_cols); + + void check_equiv(char const* objective, expr* f1, expr* f2); + + void check_contains(char const* objective, expr* f1, expr* f2); + + void verify_filter_by_negation( + expr* dst0, + relation_base const& dst, + relation_base const& neg, + unsigned_vector const& dst_eq, + unsigned_vector const& neg_eq); + }; +}; + +#endif /* CHECK_RELATION_H_ */ + diff --git a/src/muz/rel/dl_base.cpp b/src/muz/rel/dl_base.cpp index 0d4cd02d3..6dc7f2f6e 100644 --- a/src/muz/rel/dl_base.cpp +++ b/src/muz/rel/dl_base.cpp @@ -89,8 +89,7 @@ namespace datalog { void relation_base::reset() { ast_manager & m = get_plugin().get_ast_manager(); app_ref bottom_ref(m.mk_false(), m); - scoped_ptr reset_fn = - get_manager().mk_filter_interpreted_fn(static_cast(*this), bottom_ref); + scoped_ptr reset_fn = get_manager().mk_filter_interpreted_fn(*this, bottom_ref); if(!reset_fn) { NOT_IMPLEMENTED_YET(); } @@ -486,4 +485,125 @@ namespace datalog { brw.mk_or(disjs.size(), disjs.c_ptr(), fml); } + class table_plugin::min_fn : public table_min_fn{ + table_signature m_sig; + const unsigned_vector m_group_by_cols; + const unsigned m_col; + public: + min_fn(const table_signature & t_sig, const unsigned_vector& group_by_cols, const unsigned col) + : m_sig(t_sig), + m_group_by_cols(group_by_cols), + m_col(col) {} + + virtual table_base* operator()(table_base const& t) { + //return reference_implementation(t); + return reference_implementation_with_hash(t); + } + + private: + + /** + Reference implementation with negation: + + T1 = join(T, T) by group_cols + T2 = { (t1,t2) in T1 | t1[col] > t2[col] } + T3 = { t1 | (t1,t2) in T2 } + T4 = T \ T3 + + The point of this reference implementation is to show + that the minimum requires negation (set difference). + This is relevant for fixed point computations. + */ + virtual table_base * reference_implementation(const table_base & t) { + relation_manager & manager = t.get_manager(); + scoped_ptr join_fn = manager.mk_join_fn(t, t, m_group_by_cols, m_group_by_cols); + scoped_rel join_table = (*join_fn)(t, t); + + table_base::iterator join_table_it = join_table->begin(); + table_base::iterator join_table_end = join_table->end(); + table_fact row; + + table_element i, j; + + for (; join_table_it != join_table_end; ++join_table_it) { + join_table_it->get_fact(row); + i = row[m_col]; + j = row[t.num_columns() + m_col]; + + if (i > j) { + continue; + } + + join_table->remove_fact(row); + } + + unsigned_vector cols(t.num_columns()); + for (unsigned k = 0; k < cols.size(); ++k) { + cols[k] = cols.size() + k; + SASSERT(cols[k] < join_table->num_columns()); + } + + scoped_ptr project_fn = manager.mk_project_fn(*join_table, cols); + scoped_rel gt_table = (*project_fn)(*join_table); + + for (unsigned k = 0; k < cols.size(); ++k) { + cols[k] = k; + SASSERT(cols[k] < t.num_columns()); + SASSERT(cols[k] < gt_table->num_columns()); + } + + table_base * result = t.clone(); + scoped_ptr diff_fn = manager.mk_filter_by_negation_fn(*result, *gt_table, cols, cols); + (*diff_fn)(*result, *gt_table); + return result; + } + + typedef map < table_fact, table_element, svector_hash_proc, + vector_eq_proc > group_map; + + // Thanks to Nikolaj who kindly helped with the second reference implementation! + virtual table_base * reference_implementation_with_hash(const table_base & t) { + group_map group; + table_base::iterator it = t.begin(); + table_base::iterator end = t.end(); + table_fact row, row2; + table_element current_value, min_value; + for (; it != end; ++it) { + it->get_fact(row); + current_value = row[m_col]; + group_by(row, row2); + group_map::entry* entry = group.find_core(row2); + if (!entry) { + group.insert(row2, current_value); + } + else if (entry->get_data().m_value > current_value) { + entry->get_data().m_value = current_value; + } + } + table_base* result = t.get_plugin().mk_empty(m_sig); + table_base::iterator it2 = t.begin(); + for (; it2 != end; ++it2) { + it2->get_fact(row); + current_value = row[m_col]; + group_by(row, row2); + VERIFY(group.find(row2, min_value)); + if (min_value == current_value) { + result->add_fact(row); + } + } + return result; + } + + void group_by(table_fact const& in, table_fact& out) { + out.reset(); + for (unsigned i = 0; i < m_group_by_cols.size(); ++i) { + out.push_back(in[m_group_by_cols[i]]); + } + } + }; + + table_min_fn * table_plugin::mk_min_fn(const table_base & t, + unsigned_vector & group_by_cols, const unsigned col) { + return alloc(table_plugin::min_fn, t.get_signature(), group_by_cols, col); + } } diff --git a/src/muz/rel/dl_base.h b/src/muz/rel/dl_base.h index d03c94154..c26623737 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_BASE_H_ -#define _DL_BASE_H_ +#ifndef DL_BASE_H_ +#define DL_BASE_H_ #define DL_LEAK_HUNTING 0 @@ -88,7 +88,7 @@ namespace datalog { typedef typename Traits::signature signature; //this must be a vector-like type /** - The client submits an initial class to be used as a base for signature. Then we excend it by + The client submits an initial class to be used as a base for signature. Then we extend it by the common signature methods into a signature_base class which then the client inherits from to obtain the actual signature class. */ @@ -192,6 +192,29 @@ namespace datalog { virtual base_object * operator()(const base_object & t1, const base_object & t2) = 0; }; + /** + \brief Aggregate minimum value + + Informally, we want to group rows in a table \c t by \c group_by_cols and + return the minimum value in column \c col among each group. + + Let \c t be a table with N columns. + Let \c group_by_cols be a set of column identifers for table \c t such that |group_by_cols| < N. + Let \c col be a column identifier for table \c t such that \c col is not in \c group_by_cols. + + Let R_col be a set of rows in table \c t such that, for all rows r_i, r_j in R_col + and column identifiers k in \c group_by_cols, r_i[k] = r_j[k]. + + For each R_col, we want to restrict R_col to those rows whose value in column \c col is minimal. + + min_fn(R, group_by_cols, col) = + { row in R | forall row' in R . row'[group_by_cols] = row[group_by_cols] => row'[col] >= row[col] } + */ + class min_fn : public base_fn { + public: + virtual base_object * operator()(const base_object & t) = 0; + }; + class transformer_fn : public base_fn { public: virtual base_object * operator()(const base_object & t) = 0; @@ -248,6 +271,7 @@ namespace datalog { class plugin_object { friend class relation_manager; friend class check_table_plugin; + friend class check_relation_plugin; family_id m_kind; symbol m_name; @@ -465,6 +489,14 @@ namespace datalog { relation_manager & get_manager() const { return get_plugin().get_manager(); } virtual bool empty() const = 0; + /** + \brief fast emptiness check. This may be partial. + The requirement is that if fast_empty returns true + then the table or relation is in fact empty. + It is allowed to return false even if the relation is empty. + */ + virtual bool fast_empty() const { return empty(); } + virtual void add_fact(const fact & f) = 0; /** \brief Like \c add_fact, only here the caller guarantees that the fact is not present in @@ -497,6 +529,7 @@ namespace datalog { virtual unsigned get_size_estimate_rows() const { return UINT_MAX; } virtual unsigned get_size_estimate_bytes() const { return UINT_MAX; } virtual bool knows_exact_size() const { return false; } + unsigned num_columns() const { return get_signature().size(); } virtual void display(std::ostream & out) const = 0; }; @@ -846,6 +879,7 @@ namespace datalog { typedef table_infrastructure::base_fn base_table_fn; typedef table_infrastructure::join_fn table_join_fn; + typedef table_infrastructure::min_fn table_min_fn; typedef table_infrastructure::transformer_fn table_transformer_fn; typedef table_infrastructure::union_fn table_union_fn; typedef table_infrastructure::mutator_fn table_mutator_fn; @@ -1010,6 +1044,7 @@ namespace datalog { class table_plugin : public table_infrastructure::plugin_object { friend class relation_manager; + class min_fn; protected: table_plugin(symbol const& n, relation_manager & manager) : plugin_object(n, manager) {} public: @@ -1017,6 +1052,9 @@ namespace datalog { virtual bool can_handle_signature(const table_signature & s) { return s.functional_columns()==0; } protected: + virtual table_min_fn * mk_min_fn(const table_base & t, + unsigned_vector & group_by_cols, const unsigned col); + /** If the returned value is non-zero, the returned object must take ownership of \c mapper. Otherwise \c mapper must remain unmodified. @@ -1271,5 +1309,5 @@ namespace datalog { }; -#endif /* _DL_BASE_H_ */ +#endif /* DL_BASE_H_ */ diff --git a/src/muz/rel/dl_bound_relation.h b/src/muz/rel/dl_bound_relation.h index 906ba571a..485dfbafa 100644 --- a/src/muz/rel/dl_bound_relation.h +++ b/src/muz/rel/dl_bound_relation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_BOUND_RELATION_H_ -#define _DL_BOUND_RELATION_H_ +#ifndef DL_BOUND_RELATION_H_ +#define DL_BOUND_RELATION_H_ #include "dl_context.h" #include "dl_relation_manager.h" diff --git a/src/muz/rel/dl_check_table.h b/src/muz/rel/dl_check_table.h index e4f439590..35f399452 100644 --- a/src/muz/rel/dl_check_table.h +++ b/src/muz/rel/dl_check_table.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef _DL_CHECK_TABLE_H_ -#define _DL_CHECK_TABLE_H_ +#ifndef DL_CHECK_TABLE_H_ +#define DL_CHECK_TABLE_H_ #include "dl_base.h" #include "dl_decl_plugin.h" @@ -132,4 +132,4 @@ namespace datalog { }; - #endif /* _DL_CHECK_TABLE_H_ */ +#endif /* DL_CHECK_TABLE_H_ */ diff --git a/src/muz/rel/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp index 2a2507d7f..c35985e98 100644 --- a/src/muz/rel/dl_compiler.cpp +++ b/src/muz/rel/dl_compiler.cpp @@ -33,7 +33,6 @@ namespace datalog { void compiler::reset() { m_pred_regs.reset(); - m_new_reg = 0; } void compiler::ensure_predicate_loaded(func_decl * pred, instruction_block & acc) { @@ -51,16 +50,16 @@ namespace datalog { } void compiler::make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result, - instruction_block & acc) { + bool reuse_t1, instruction_block & acc) { relation_signature res_sig; relation_signature::from_join(m_reg_signatures[t1], m_reg_signatures[t2], vars.size(), vars.get_cols1(), vars.get_cols2(), res_sig); - result = get_fresh_register(res_sig); + result = get_register(res_sig, reuse_t1, t1); acc.push_back(instruction::mk_join(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), result)); } void compiler::make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, - const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { + const unsigned_vector & removed_cols, reg_idx & result, bool reuse_t1, instruction_block & acc) { relation_signature aux_sig; relation_signature sig1 = m_reg_signatures[t1]; relation_signature sig2 = m_reg_signatures[t2]; @@ -68,29 +67,35 @@ namespace datalog { relation_signature res_sig; relation_signature::from_project(aux_sig, removed_cols.size(), removed_cols.c_ptr(), res_sig); - result = get_fresh_register(res_sig); + result = get_register(res_sig, reuse_t1, t1); acc.push_back(instruction::mk_join_project(t1, t2, vars.size(), vars.get_cols1(), vars.get_cols2(), removed_cols.size(), removed_cols.c_ptr(), result)); } + void compiler::make_min(reg_idx source, reg_idx & target, const unsigned_vector & group_by_cols, + const unsigned min_col, instruction_block & acc) { + target = get_register(m_reg_signatures[source], true, source); + acc.push_back(instruction::mk_min(source, target, group_by_cols, min_col)); + } + void compiler::make_filter_interpreted_and_project(reg_idx src, app_ref & cond, - const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc) { + const unsigned_vector & removed_cols, reg_idx & result, bool reuse, instruction_block & acc) { SASSERT(!removed_cols.empty()); relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], removed_cols.size(), removed_cols.c_ptr(), res_sig); - result = get_fresh_register(res_sig); + result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_filter_interpreted_and_project(src, cond, removed_cols.size(), removed_cols.c_ptr(), result)); } - void compiler::make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, - reg_idx & result, instruction_block & acc) { + void compiler::make_select_equal_and_project(reg_idx src, const relation_element val, unsigned col, + reg_idx & result, bool reuse, instruction_block & acc) { relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], 1, &col, res_sig); - result = get_fresh_register(res_sig); + result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_select_equal_and_project(m_context.get_manager(), src, val, col, result)); } @@ -115,12 +120,12 @@ namespace datalog { } void compiler::make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, - reg_idx & result, instruction_block & acc) { + reg_idx & result, bool reuse, instruction_block & acc) { SASSERT(col_cnt>0); relation_signature res_sig; relation_signature::from_project(m_reg_signatures[src], col_cnt, removed_cols, res_sig); - result = get_fresh_register(res_sig); + result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_projection(src, col_cnt, removed_cols, result)); } @@ -132,7 +137,15 @@ namespace datalog { return result; } - compiler::reg_idx compiler::get_single_column_register(const relation_sort & s) { + compiler::reg_idx compiler::get_register(const relation_signature & sig, bool reuse, compiler::reg_idx r) { + if (!reuse) + return get_fresh_register(sig); + SASSERT(r != execution_context::void_register); + m_reg_signatures[r] = sig; + return r; + } + + compiler::reg_idx compiler::get_single_column_register(const relation_sort s) { relation_signature singl_sig; singl_sig.push_back(s); return get_fresh_register(singl_sig); @@ -158,7 +171,7 @@ namespace datalog { } } - void compiler::make_add_constant_column(func_decl* head_pred, reg_idx src, const relation_sort & s, const relation_element & val, + void compiler::make_add_constant_column(func_decl* head_pred, reg_idx src, const relation_sort s, const relation_element val, reg_idx & result, bool & dealloc, instruction_block & acc) { reg_idx singleton_table; if(!m_constant_registers.find(s, val, singleton_table)) { @@ -169,22 +182,22 @@ namespace datalog { } if(src==execution_context::void_register) { result = singleton_table; - dealloc = false; + SASSERT(dealloc == false); } else { variable_intersection empty_vars(m_context.get_manager()); - make_join(src, singleton_table, empty_vars, result, acc); + make_join(src, singleton_table, empty_vars, result, dealloc, acc); dealloc = true; } } - void compiler::make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result, + void compiler::make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort s, reg_idx & result, bool & dealloc, instruction_block & acc) { TRACE("dl", tout << "Adding unbound column " << mk_pp(pred, m_context.get_manager()) << "\n";); IF_VERBOSE(3, { expr_ref e(m_context.get_manager()); - compiled_rule->to_formula(e); + m_context.get_rule_manager().to_formula(*compiled_rule, e); verbose_stream() << "Compiling unsafe rule column " << col_idx << "\n" << mk_ismt2_pp(e, m_context.get_manager()) << "\n"; }); @@ -198,11 +211,11 @@ namespace datalog { } if(src == execution_context::void_register) { result = total_table; - dealloc = false; + SASSERT(dealloc == false); } else { variable_intersection empty_vars(m_context.get_manager()); - make_join(src, total_table, empty_vars, result, acc); + make_join(src, total_table, empty_vars, result, dealloc, acc); dealloc = true; } } @@ -221,7 +234,7 @@ namespace datalog { void compiler::make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, - instruction_block & acc) { + bool reuse, instruction_block & acc) { relation_signature & src_sig = m_reg_signatures[src]; reg_idx single_col_reg; @@ -236,19 +249,20 @@ namespace datalog { removed_cols.push_back(i); } } - make_projection(src, removed_cols.size(), removed_cols.c_ptr(), single_col_reg, acc); + make_projection(src, removed_cols.size(), removed_cols.c_ptr(), single_col_reg, false, acc); } variable_intersection vi(m_context.get_manager()); vi.add_pair(col, 0); - make_join(src, single_col_reg, vi, result, acc); - make_dealloc_non_void(single_col_reg, acc); + make_join(src, single_col_reg, vi, result, reuse, acc); + if (src_col_cnt != 1) + make_dealloc_non_void(single_col_reg, acc); } void compiler::make_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, - reg_idx & result, instruction_block & acc) { + reg_idx & result, bool reuse, instruction_block & acc) { relation_signature res_sig; relation_signature::from_rename(m_reg_signatures[src], cycle_len, permutation_cycle, res_sig); - result = get_fresh_register(res_sig); + result = get_register(res_sig, reuse, src); acc.push_back(instruction::mk_rename(src, cycle_len, permutation_cycle, result)); } @@ -301,12 +315,8 @@ namespace datalog { new_src_col_offset.push_back(src_cols_to_remove.size()); } if(!src_cols_to_remove.empty()) { - reg_idx new_curr; - make_projection(curr, src_cols_to_remove.size(), src_cols_to_remove.c_ptr(), new_curr, acc); - if (dealloc) - make_dealloc_non_void(curr, acc); + make_projection(curr, src_cols_to_remove.size(), src_cols_to_remove.c_ptr(), curr, dealloc, acc); dealloc = true; - curr=new_curr; curr_sig = & m_reg_signatures[curr]; //update ACK_BOUND_VAR references @@ -325,21 +335,15 @@ namespace datalog { } unsigned bound_column_index; if(acis[i].kind!=ACK_UNBOUND_VAR || !handled_unbound.find(acis[i].var_index,bound_column_index)) { - reg_idx new_curr; - bool new_dealloc; bound_column_index=curr_sig->size(); if(acis[i].kind==ACK_CONSTANT) { - make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, new_curr, new_dealloc, acc); + make_add_constant_column(head_pred, curr, acis[i].domain, acis[i].constant, curr, dealloc, acc); } else { SASSERT(acis[i].kind==ACK_UNBOUND_VAR); - make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, new_curr, new_dealloc, acc); + make_add_unbound_column(compiled_rule, i, head_pred, curr, acis[i].domain, curr, dealloc, acc); handled_unbound.insert(acis[i].var_index,bound_column_index); } - if (dealloc) - make_dealloc_non_void(curr, acc); - dealloc = new_dealloc; - curr=new_curr; curr_sig = & m_reg_signatures[curr]; SASSERT(bound_column_index==curr_sig->size()-1); } @@ -357,12 +361,8 @@ namespace datalog { used_cols.insert(col); continue; } - reg_idx new_curr; - make_duplicate_column(curr, col, new_curr, acc); - if (dealloc) - make_dealloc_non_void(curr, acc); + make_duplicate_column(curr, col, curr, dealloc, acc); dealloc = true; - curr=new_curr; curr_sig = & m_reg_signatures[curr]; unsigned bound_column_index=curr_sig->size()-1; SASSERT((*curr_sig)[bound_column_index]==acis[i].domain); @@ -387,12 +387,8 @@ namespace datalog { SASSERT(permutation.size()<=col_cnt); //this should not be an infinite loop } while(next!=i); - reg_idx new_curr; - make_rename(curr, permutation.size(), permutation.c_ptr(), new_curr, acc); - if (dealloc) - make_dealloc_non_void(curr, acc); + make_rename(curr, permutation.size(), permutation.c_ptr(), curr, dealloc, acc); dealloc = true; - curr=new_curr; curr_sig = & m_reg_signatures[curr]; } @@ -408,29 +404,72 @@ namespace datalog { void compiler::get_local_indexes_for_projection(app * t, var_counter & globals, unsigned ofs, unsigned_vector & res) { + // TODO: this can be optimized to avoid renames in some cases unsigned n = t->get_num_args(); for(unsigned i = 0; iget_arg(i); - if(!is_var(e) || globals.get(to_var(e)->get_idx())!=0) { - continue; + if (is_var(e) && globals.get(to_var(e)->get_idx()) > 0) { + globals.update(to_var(e)->get_idx(), -1); + res.push_back(i + ofs); } - res.push_back(i+ofs); } } void compiler::get_local_indexes_for_projection(rule * r, unsigned_vector & res) { SASSERT(r->get_positive_tail_size()==2); - ast_manager & m = m_context.get_manager(); rule_counter counter; - counter.count_rule_vars(m, r); + // leave one column copy per var in the head (avoids later duplication) + counter.count_vars(r->get_head(), -1); + + // take interp & neg preds into account (at least 1 column copy if referenced) + unsigned n = r->get_tail_size(); + if (n > 2) { + rule_counter counter_tail; + for (unsigned i = 2; i < n; ++i) { + counter_tail.count_vars(r->get_tail(i)); + } + + rule_counter::iterator I = counter_tail.begin(), E = counter_tail.end(); + for (; I != E; ++I) { + int& n = counter.get(I->m_key); + if (n == 0) + n = -1; + } + } + app * t1 = r->get_tail(0); app * t2 = r->get_tail(1); - counter.count_vars(m, t1, -1); - counter.count_vars(m, t2, -1); + counter.count_vars(t1); + counter.count_vars(t2); + get_local_indexes_for_projection(t1, counter, 0, res); get_local_indexes_for_projection(t2, counter, t1->get_num_args(), res); } + void compiler::find_min_aggregates(const rule * r, ptr_vector& min_aggregates) { + unsigned ut_len = r->get_uninterpreted_tail_size(); + unsigned ft_len = r->get_tail_size(); // full tail + func_decl * aggregate; + for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { + aggregate = r->get_tail(tail_index)->get_decl(); + if (dl_decl_plugin::is_aggregate(aggregate)) { + min_aggregates.push_back(aggregate); + } + } + } + + bool compiler::prepare_min_aggregate(const func_decl * decl, const ptr_vector& min_aggregates, + unsigned_vector & group_by_cols, unsigned & min_col) { + for (unsigned i = 0; i < min_aggregates.size(); ++i) { + if (dl_decl_plugin::min_func_decl(min_aggregates[i]) == decl) { + group_by_cols = dl_decl_plugin::group_by_cols(min_aggregates[i]); + min_col = dl_decl_plugin::min_col(min_aggregates[i]); + return true; + } + } + return false; + } + void compiler::compile_rule_evaluation_run(rule * r, reg_idx head_reg, const reg_idx * tail_regs, reg_idx delta_reg, bool use_widening, instruction_block & acc) { @@ -456,6 +495,12 @@ namespace datalog { // whether to dealloc the previous result bool dealloc = true; + // setup information for min aggregation + ptr_vector min_aggregates; + find_min_aggregates(r, min_aggregates); + unsigned_vector group_by_cols; + unsigned min_col; + if(pt_len == 2) { reg_idx t1_reg=tail_regs[0]; reg_idx t2_reg=tail_regs[1]; @@ -464,6 +509,14 @@ namespace datalog { SASSERT(m_reg_signatures[t1_reg].size()==a1->get_num_args()); SASSERT(m_reg_signatures[t2_reg].size()==a2->get_num_args()); + if (prepare_min_aggregate(a1->get_decl(), min_aggregates, group_by_cols, min_col)) { + make_min(t1_reg, single_res, group_by_cols, min_col, acc); + } + + if (prepare_min_aggregate(a2->get_decl(), min_aggregates, group_by_cols, min_col)) { + make_min(t2_reg, single_res, group_by_cols, min_col, acc); + } + variable_intersection a1a2(m_context.get_manager()); a1a2.populate(a1,a2); @@ -471,10 +524,10 @@ namespace datalog { get_local_indexes_for_projection(r, removed_cols); if(removed_cols.empty()) { - make_join(t1_reg, t2_reg, a1a2, single_res, acc); + make_join(t1_reg, t2_reg, a1a2, single_res, false, acc); } else { - make_join_project(t1_reg, t2_reg, a1a2, removed_cols, single_res, acc); + make_join_project(t1_reg, t2_reg, a1a2, removed_cols, single_res, false, acc); } unsigned rem_index = 0; @@ -501,11 +554,15 @@ namespace datalog { SASSERT(rem_index==rem_sz); } else if(pt_len==1) { - reg_idx t_reg=tail_regs[0]; app * a = r->get_tail(0); - SASSERT(m_reg_signatures[t_reg].size()==a->get_num_args()); + single_res = tail_regs[0]; + dealloc = false; - single_res = t_reg; + if (prepare_min_aggregate(a->get_decl(), min_aggregates, group_by_cols, min_col)) { + make_min(single_res, single_res, group_by_cols, min_col, acc); + } + + SASSERT(m_reg_signatures[single_res].size() == a->get_num_args()); unsigned n=a->get_num_args(); for(unsigned i=0; iget_uninterpreted_tail_size(); unsigned ft_len = r->get_tail_size(); // full tail ptr_vector tail; for (unsigned tail_index = ut_len; tail_index < ft_len; ++tail_index) { - tail.push_back(r->get_tail(tail_index)); + if (!r->is_min_tail(tail_index)) + tail.push_back(r->get_tail(tail_index)); } + expr_ref_vector binding(m); if (!tail.empty()) { app_ref filter_cond(tail.size() == 1 ? to_app(tail.back()) : m.mk_and(tail.size(), tail.c_ptr()), m); - ptr_vector filter_vars; - get_free_vars(filter_cond, filter_vars); - + m_free_vars(filter_cond); // create binding - expr_ref_vector binding(m); - binding.resize(filter_vars.size()+1); - - for (unsigned v = 0; v < filter_vars.size(); ++v) { - if (!filter_vars[v]) + binding.resize(m_free_vars.size()+1); + for (unsigned v = 0; v < m_free_vars.size(); ++v) { + if (!m_free_vars[v]) continue; int2ints::entry * entry = var_indexes.find_core(v); @@ -616,15 +665,8 @@ namespace datalog { src_col = entry->get_data().m_value.back(); } else { // we have an unbound variable, so we add an unbound column for it - relation_sort unbound_sort = filter_vars[v]; - - reg_idx new_reg; - bool new_dealloc; - make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, new_reg, new_dealloc, acc); - if (dealloc) - make_dealloc_non_void(filtered_res, acc); - dealloc = new_dealloc; - filtered_res = new_reg; + relation_sort unbound_sort = m_free_vars[v]; + make_add_unbound_column(r, 0, head_pred, filtered_res, unbound_sort, filtered_res, dealloc, acc); src_col = single_res_expr.size(); single_res_expr.push_back(m.mk_var(v, unbound_sort)); @@ -633,19 +675,63 @@ namespace datalog { entry->get_data().m_value.push_back(src_col); } relation_sort var_sort = m_reg_signatures[filtered_res][src_col]; - binding[filter_vars.size()-v] = m.mk_var(src_col, var_sort); + binding[m_free_vars.size()-v] = m.mk_var(src_col, var_sort); } + } + + // add at least one column for the negative filter + if (pt_len != ut_len && filtered_res == execution_context::void_register) { + relation_signature empty_signature; + make_full_relation(head_pred, empty_signature, filtered_res, acc); + } + + //enforce negative predicates + for (unsigned i = pt_len; iget_tail(i); + func_decl * neg_pred = neg_tail->get_decl(); + variable_intersection neg_intersection(m_context.get_manager()); + neg_intersection.populate(single_res_expr, neg_tail); + unsigned_vector t_cols(neg_intersection.size(), neg_intersection.get_cols1()); + unsigned_vector neg_cols(neg_intersection.size(), neg_intersection.get_cols2()); + + unsigned neg_len = neg_tail->get_num_args(); + for (unsigned i = 0; iget_arg(i); + if (is_var(e)) { + continue; + } + SASSERT(is_app(e)); + relation_sort arg_sort; + m_context.get_rel_context()->get_rmanager().from_predicate(neg_pred, i, arg_sort); + make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), filtered_res, dealloc, acc); + + t_cols.push_back(single_res_expr.size()); + neg_cols.push_back(i); + single_res_expr.push_back(e); + } + SASSERT(t_cols.size() == neg_cols.size()); + + reg_idx neg_reg = m_pred_regs.find(neg_pred); + if (!dealloc) + make_clone(filtered_res, filtered_res, acc); + acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), + t_cols.c_ptr(), neg_cols.c_ptr())); + dealloc = true; + } + + // enforce interpreted tail predicates + if (!tail.empty()) { + app_ref filter_cond(tail.size() == 1 ? to_app(tail.back()) : m.mk_and(tail.size(), tail.c_ptr()), m); // check if there are any columns to remove unsigned_vector remove_columns; { unsigned_vector var_idx_to_remove; - ptr_vector vars; - get_free_vars(r->get_head(), vars); + m_free_vars(r->get_head()); for (int2ints::iterator I = var_indexes.begin(), E = var_indexes.end(); I != E; ++I) { unsigned var_idx = I->m_key; - if (!vars.get(var_idx, 0)) { + if (!m_free_vars.contains(var_idx)) { unsigned_vector & cols = I->m_value; for (unsigned i = 0; i < cols.size(); ++i) { remove_columns.push_back(cols[i]); @@ -687,57 +773,12 @@ namespace datalog { make_clone(filtered_res, filtered_res, acc); acc.push_back(instruction::mk_filter_interpreted(filtered_res, app_renamed)); } else { - reg_idx new_reg; std::sort(remove_columns.begin(), remove_columns.end()); - make_filter_interpreted_and_project(filtered_res, app_renamed, remove_columns, new_reg, acc); - if (dealloc) - make_dealloc_non_void(filtered_res, acc); - filtered_res = new_reg; + make_filter_interpreted_and_project(filtered_res, app_renamed, remove_columns, filtered_res, dealloc, acc); } dealloc = true; } - //enforce negative predicates - for (unsigned i = pt_len; iget_tail(i); - func_decl * neg_pred = neg_tail->get_decl(); - variable_intersection neg_intersection(m_context.get_manager()); - neg_intersection.populate(single_res_expr, neg_tail); - unsigned_vector t_cols(neg_intersection.size(), neg_intersection.get_cols1()); - unsigned_vector neg_cols(neg_intersection.size(), neg_intersection.get_cols2()); - - unsigned neg_len = neg_tail->get_num_args(); - for (unsigned i = 0; iget_arg(i); - if (is_var(e)) { - continue; - } - SASSERT(is_app(e)); - relation_sort arg_sort; - m_context.get_rel_context()->get_rmanager().from_predicate(neg_pred, i, arg_sort); - reg_idx new_reg; - bool new_dealloc; - make_add_constant_column(head_pred, filtered_res, arg_sort, to_app(e), new_reg, new_dealloc, acc); - - if (dealloc) - make_dealloc_non_void(filtered_res, acc); - dealloc = new_dealloc; - filtered_res = new_reg; // here filtered_res value gets changed !! - - t_cols.push_back(single_res_expr.size()); - neg_cols.push_back(i); - single_res_expr.push_back(e); - } - SASSERT(t_cols.size() == neg_cols.size()); - - reg_idx neg_reg = m_pred_regs.find(neg_pred); - if (!dealloc) - make_clone(filtered_res, filtered_res, acc); - acc.push_back(instruction::mk_filter_by_negation(filtered_res, neg_reg, t_cols.size(), - t_cols.c_ptr(), neg_cols.c_ptr())); - dealloc = true; - } - #if 0 // this version is potentially better for non-symbolic tables, // since it constraints each unbound column at a time (reducing the @@ -745,10 +786,9 @@ namespace datalog { unsigned ft_len=r->get_tail_size(); //full tail for(unsigned tail_index=ut_len; tail_indexget_tail(tail_index); - ptr_vector t_vars; - ::get_free_vars(t, t_vars); + m_free_vars(t); - if(t_vars.empty()) { + if (m_free_vars.empty()) { expr_ref simplified(m); m_context.get_rewriter()(t, simplified); if(m.is_true(simplified)) { @@ -761,23 +801,22 @@ namespace datalog { } //determine binding size - while (!t_vars.back()) { - t_vars.pop_back(); - } - unsigned max_var = t_vars.size(); + + unsigned max_var = m_free_vars.size(); + while (max_var > 0 && !m_free_vars[max_var-1]) --max_var; //create binding expr_ref_vector binding(m); - binding.resize(max_var+1); + binding.resize(max_var); - for(unsigned v = 0; v < t_vars.size(); ++v) { - if (!t_vars[v]) { + for(unsigned v = 0; v < max_var; ++v) { + if (!m_free_vars[v]) { continue; } int2ints::entry * e = var_indexes.find_core(v); if(!e) { //we have an unbound variable, so we add an unbound column for it - relation_sort unbound_sort = t_vars[v]; + relation_sort unbound_sort = m_free_vars[v]; reg_idx new_reg; TRACE("dl", tout << mk_pp(head_pred, m_context.get_manager()) << "\n";); @@ -872,9 +911,11 @@ namespace datalog { ast_manager& m = m_context.get_manager(); unsigned pt_len = r->get_positive_tail_size(); unsigned ut_len = r->get_uninterpreted_tail_size(); - if (pt_len == ut_len || pt_len == 0) { + + // no negated predicates + if (pt_len == ut_len) return; - } + // populate negative variables: for (unsigned i = pt_len; i < ut_len; ++i) { app * neg_tail = r->get_tail(i); @@ -901,13 +942,7 @@ namespace datalog { expr* e = it->m_value; if (!pos_vars.contains(v)) { single_res_expr.push_back(e); - reg_idx new_single_res; - bool new_dealloc; - make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), new_single_res, new_dealloc, acc); - if (dealloc) - make_dealloc_non_void(single_res, acc); - dealloc = new_dealloc; - single_res = new_single_res; + make_add_unbound_column(r, v, pred, single_res, m.get_sort(e), single_res, dealloc, acc); TRACE("dl", tout << "Adding unbound column: " << mk_pp(e, m) << "\n";); } } @@ -1338,7 +1373,7 @@ namespace datalog { acc.set_observer(0); - TRACE("dl", execution_code.display(*m_context.get_rel_context(), tout);); + TRACE("dl", execution_code.display(execution_context(m_context), tout);); } diff --git a/src/muz/rel/dl_compiler.h b/src/muz/rel/dl_compiler.h index e0f9af424..51be65d01 100644 --- a/src/muz/rel/dl_compiler.h +++ b/src/muz/rel/dl_compiler.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_COMPILER_H_ -#define _DL_COMPILER_H_ +#ifndef DL_COMPILER_H_ +#define DL_COMPILER_H_ #include #include @@ -82,9 +82,11 @@ namespace datalog { relation_sort domain; // domain of the column assembling_column_kind kind; // "instruction" tag - unsigned source_column; // for ACK_BOUND_VAR - unsigned var_index; // for ACK_UNBOUND_VAR - relation_element constant; // for ACK_CONSTANT + union { + unsigned source_column; // for ACK_BOUND_VAR + unsigned var_index; // for ACK_UNBOUND_VAR + relation_element constant; // for ACK_CONSTANT + }; }; class instruction_observer : public instruction_block::instruction_observer { @@ -111,12 +113,28 @@ namespace datalog { */ instruction_block & m_top_level_code; pred2idx m_pred_regs; - reg_idx m_new_reg; - vector m_reg_signatures; - obj_pair_map m_constant_registers; + vector m_reg_signatures; + obj_pair_map m_constant_registers; obj_pair_map m_total_registers; - obj_map m_empty_tables_registers; - instruction_observer m_instruction_observer; + obj_map m_empty_tables_registers; + instruction_observer m_instruction_observer; + expr_free_vars m_free_vars; + + /** + \brief Finds all the min aggregation functions in the premise of a given rule. + */ + static void find_min_aggregates(const rule * r, ptr_vector& min_aggregates); + + /** + \brief Decides whether a predicate is subject to a min aggregation function. + + If \c decl is subject to a min aggregation function, the output parameters are written + with the neccessary information. + + \returns true if the output paramaters have been written + */ + static bool prepare_min_aggregate(const func_decl * decl, const ptr_vector& min_aggregates, + unsigned_vector & group_by_cols, unsigned & min_col); /** If true, the union operation on the underlying structure only provides the information @@ -132,7 +150,8 @@ namespace datalog { bool compile_with_widening() const { return m_context.compile_with_widening(); } reg_idx get_fresh_register(const relation_signature & sig); - reg_idx get_single_column_register(const relation_sort & s); + reg_idx get_register(const relation_signature & sig, bool reuse, reg_idx r); + reg_idx get_single_column_register(const relation_sort s); /** \brief Allocate registers for predicates in \c pred and add them into the \c regs map. @@ -142,21 +161,23 @@ namespace datalog { void get_fresh_registers(const func_decl_set & preds, pred2idx & regs); void make_join(reg_idx t1, reg_idx t2, const variable_intersection & vars, reg_idx & result, - instruction_block & acc); + bool reuse_t1, instruction_block & acc); + void make_min(reg_idx source, reg_idx & target, const unsigned_vector & group_by_cols, + const unsigned min_col, instruction_block & acc); void make_join_project(reg_idx t1, reg_idx t2, const variable_intersection & vars, - const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc); + const unsigned_vector & removed_cols, reg_idx & result, bool reuse_t1, instruction_block & acc); void make_filter_interpreted_and_project(reg_idx src, app_ref & cond, - const unsigned_vector & removed_cols, reg_idx & result, instruction_block & acc); - void make_select_equal_and_project(reg_idx src, const relation_element & val, unsigned col, - reg_idx & result, instruction_block & acc); + const unsigned_vector & removed_cols, reg_idx & result, bool reuse, instruction_block & acc); + void make_select_equal_and_project(reg_idx src, const relation_element val, unsigned col, + reg_idx & result, bool reuse, instruction_block & acc); /** \brief Create add an union or widen operation and put it into \c acc. */ void make_union(reg_idx src, reg_idx tgt, reg_idx delta, bool widening, instruction_block & acc); void make_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols, - reg_idx & result, instruction_block & acc); + reg_idx & result, bool reuse, instruction_block & acc); void make_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, - reg_idx & result, instruction_block & acc); + reg_idx & result, bool reuse, instruction_block & acc); void make_clone(reg_idx src, reg_idx & result, instruction_block & acc); /** @@ -171,10 +192,10 @@ namespace datalog { void make_dealloc_non_void(reg_idx r, instruction_block & acc); - void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort & s, const relation_element & val, + void make_add_constant_column(func_decl* pred, reg_idx src, const relation_sort s, const relation_element val, reg_idx & result, bool & dealloc, instruction_block & acc); - void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort & s, reg_idx & result, + void make_add_unbound_column(rule* compiled_rule, unsigned col_idx, func_decl* pred, reg_idx src, const relation_sort s, reg_idx & result, bool & dealloc, instruction_block & acc); void make_full_relation(func_decl* pred, const relation_signature & sig, reg_idx & result, instruction_block & acc); @@ -182,7 +203,7 @@ namespace datalog { void add_unbound_columns_for_negation(rule* compiled_rule, func_decl* pred, reg_idx& single_res, expr_ref_vector& single_res_expr, bool & dealloc, instruction_block& acc); - void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, instruction_block & acc); + void make_duplicate_column(reg_idx src, unsigned col, reg_idx & result, bool reuse, instruction_block & acc); void ensure_predicate_loaded(func_decl * pred, instruction_block & acc); @@ -278,5 +299,5 @@ namespace datalog { }; -#endif /* _DL_COMPILER_H_ */ +#endif /* DL_COMPILER_H_ */ diff --git a/src/muz/rel/dl_external_relation.h b/src/muz/rel/dl_external_relation.h index 03838ecc4..0bd78b776 100644 --- a/src/muz/rel/dl_external_relation.h +++ b/src/muz/rel/dl_external_relation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_EXTERNAL_RELATION_H_ -#define _DL_EXTERNAL_RELATION_H_ +#ifndef DL_EXTERNAL_RELATION_H_ +#define DL_EXTERNAL_RELATION_H_ #include "dl_base.h" diff --git a/src/muz/rel/dl_finite_product_relation.h b/src/muz/rel/dl_finite_product_relation.h index d5181d122..e91ab4a17 100644 --- a/src/muz/rel/dl_finite_product_relation.h +++ b/src/muz/rel/dl_finite_product_relation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_FINITE_PRODUCT_RELATION_H_ -#define _DL_FINITE_PRODUCT_RELATION_H_ +#ifndef DL_FINITE_PRODUCT_RELATION_H_ +#define DL_FINITE_PRODUCT_RELATION_H_ #include "dl_base.h" @@ -362,5 +362,5 @@ namespace datalog { }; -#endif /* _DL_FINITE_PRODUCT_RELATION_H_ */ +#endif /* DL_FINITE_PRODUCT_RELATION_H_ */ diff --git a/src/muz/rel/dl_instruction.cpp b/src/muz/rel/dl_instruction.cpp index a702c27ce..fb7bb9281 100644 --- a/src/muz/rel/dl_instruction.cpp +++ b/src/muz/rel/dl_instruction.cpp @@ -25,6 +25,7 @@ Revision History: #include"rel_context.h" #include"debug.h" #include"warning.h" +#include"dl_table_relation.h" namespace datalog { @@ -37,8 +38,7 @@ namespace datalog { execution_context::execution_context(context & context) : m_context(context), m_stopwatch(0), - m_timelimit_ms(0), - m_eager_emptiness_checking(context.eager_emptiness_checking()) {} + m_timelimit_ms(0) {} execution_context::~execution_context() { reset(); @@ -63,6 +63,10 @@ namespace datalog { return dynamic_cast(*m_context.get_rel_context()); } + rel_context const& execution_context::get_rel_context() const { + return dynamic_cast(*m_context.get_rel_context()); + } + struct compare_size_proc { typedef std::pair pr; bool operator()(pr const& a, pr const& b) const { @@ -122,6 +126,22 @@ namespace datalog { m_timelimit_ms < static_cast(1000*m_stopwatch->get_current_seconds())); } + void execution_context::collect_statistics(statistics& st) const { + st.update("dl.joins", m_stats.m_join); + st.update("dl.project", m_stats.m_project); + st.update("dl.filter", m_stats.m_filter); + st.update("dl.total", m_stats.m_total); + st.update("dl.unary_singleton", m_stats.m_unary_singleton); + st.update("dl.filter_by_negation", m_stats.m_filter_by_negation); + st.update("dl.select_equal_project", m_stats.m_select_equal_project); + st.update("dl.join_project", m_stats.m_join_project); + st.update("dl.project_rename", m_stats.m_project_rename); + st.update("dl.union", m_stats.m_union); + st.update("dl.filter_interpreted_project", m_stats.m_filter_interp_project); + st.update("dl.filter_id", m_stats.m_filter_id); + st.update("dl.filter_eq", m_stats.m_filter_eq); + } + // ----------------------------------- // @@ -141,17 +161,29 @@ namespace datalog { process_costs(); } - void instruction::display_indented(rel_context_base const & _ctx, std::ostream & out, std::string indentation) const { + void instruction::collect_statistics(statistics& st) const { + costs c; + get_total_cost(c); + st.update("instruction", c.instructions); + st.update("instruction-time", c.milliseconds); + } + + + void instruction::display_indented(execution_context const & _ctx, std::ostream & out, std::string indentation) const { out << indentation; - rel_context const& ctx = dynamic_cast(_ctx); - display_head_impl(ctx, out); + rel_context const& ctx = _ctx.get_rel_context(); + display_head_impl(_ctx, out); if (ctx.output_profile()) { out << " {"; output_profile(out); out << '}'; } out << "\n"; - display_body_impl(ctx, out, indentation); + display_body_impl(_ctx, out, indentation); + } + + void instruction::log_verbose(execution_context& ctx) { + IF_VERBOSE(2, display(ctx, verbose_stream());); } class instr_io : public instruction { @@ -162,6 +194,7 @@ namespace datalog { instr_io(bool store, func_decl_ref pred, reg_idx reg) : m_store(store), m_pred(pred), m_reg(reg) {} virtual bool perform(execution_context & ctx) { + log_verbose(ctx); if (m_store) { if (ctx.reg(m_reg)) { ctx.get_rel_context().store_relation(m_pred, ctx.release_reg(m_reg)); @@ -177,7 +210,7 @@ namespace datalog { } else { relation_base& rel = ctx.get_rel_context().get_relation(m_pred); - if ((!ctx.eager_emptiness_checking() || !rel.empty())) { + if (!rel.fast_empty()) { ctx.set_reg(m_reg, rel.clone()); } else { @@ -189,7 +222,7 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, m_pred->get_name().bare_str()); } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { const char * rel_name = m_pred->get_name().bare_str(); if (m_store) { out << "store " << m_reg << " into " << rel_name; @@ -220,7 +253,7 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, "alloc"); } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "dealloc " << m_reg; } }; @@ -237,7 +270,7 @@ namespace datalog { instr_clone_move(bool clone, reg_idx src, reg_idx tgt) : m_clone(clone), m_src(src), m_tgt(tgt) {} virtual bool perform(execution_context & ctx) { - ctx.make_empty(m_tgt); + if (ctx.reg(m_src)) log_verbose(ctx); if (m_clone) { ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.reg(m_src)->clone() : 0); } @@ -255,7 +288,7 @@ namespace datalog { ctx.set_register_annotation(m_src, str); } } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << (m_clone ? "clone " : "move ") << m_src << " into " << m_tgt; } }; @@ -278,7 +311,7 @@ namespace datalog { idx_vector::const_iterator end=m_controls.end(); for(; it != end; ++it) { reg_idx r = *it; - if (ctx.reg(r) && !ctx.reg(r)->empty()) { + if (ctx.reg(r) && !ctx.reg(r)->fast_empty()) { return false; } } @@ -296,6 +329,7 @@ namespace datalog { dealloc(m_body); } virtual bool perform(execution_context & ctx) { + log_verbose(ctx); TRACE("dl", tout << "loop entered\n";); unsigned count = 0; while (!control_is_empty(ctx)) { @@ -311,11 +345,11 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { m_body->make_annotations(ctx); } - virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const & ctx, std::ostream & out) const { out << "while"; print_container(m_controls, out); } - virtual void display_body_impl(rel_context_base const & ctx, std::ostream & out, std::string indentation) const { + virtual void display_body_impl(execution_context const & ctx, std::ostream & out, std::string indentation) const { m_body->display_indented(ctx, out, indentation+" "); } }; @@ -339,8 +373,10 @@ namespace datalog { : m_rel1(rel1), m_rel2(rel2), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2), m_res(result) {} virtual bool perform(execution_context & ctx) { - ctx.make_empty(m_res); + log_verbose(ctx); + ++ctx.m_stats.m_join; if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { + ctx.make_empty(m_res); return true; } relation_join_fn * fn; @@ -349,8 +385,9 @@ namespace datalog { if (!find_fn(r1, r2, fn)) { fn = r1.get_manager().mk_join_fn(r1, r2, m_cols1, m_cols2); if (!fn) { - throw default_exception("trying to perform unsupported join operation on relations of kinds %s and %s", - r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); + throw default_exception(default_exception::fmt(), + "trying to perform unsupported join operation on relations of kinds %s and %s", + r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); } store_fn(r1, r2, fn); } @@ -367,7 +404,7 @@ namespace datalog { ctx.reg(m_res)->get_signature().output(ctx.get_rel_context().get_manager(), tout); tout<<":"<get_size_estimate_rows()<<"\n";); - if (ctx.eager_emptiness_checking() && ctx.reg(m_res)->empty()) { + if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } return true; @@ -378,7 +415,7 @@ namespace datalog { ctx.get_register_annotation(m_rel1, a1); ctx.set_register_annotation(m_res, "join " + a1 + " " + a2); } - virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const & ctx, std::ostream & out) const { out << "join " << m_rel1; print_container(m_cols1, out); out << " and " << m_rel2; @@ -400,6 +437,8 @@ namespace datalog { instr_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col) : m_reg(reg), m_value(value, m), m_col(col) {} virtual bool perform(execution_context & ctx) { + log_verbose(ctx); + ++ctx.m_stats.m_filter_eq; if (!ctx.reg(m_reg)) { return true; } @@ -409,7 +448,7 @@ namespace datalog { if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_equal_fn(r, m_value, m_col); if (!fn) { - throw default_exception( + throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_equal operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } @@ -417,7 +456,7 @@ namespace datalog { } (*fn)(r); - if (ctx.eager_emptiness_checking() && r.empty()) { + if (r.fast_empty()) { ctx.make_empty(m_reg); } return true; @@ -427,9 +466,9 @@ namespace datalog { a << "filter_equal " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); ctx.set_register_annotation(m_reg, a.str()); } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_equal " << m_reg << " col: " << m_col << " val: " - << ctx.get_rmanager().to_nice_string(m_value); + << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); } }; @@ -447,6 +486,8 @@ namespace datalog { instr_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols) : m_reg(reg), m_cols(col_cnt, identical_cols) {} virtual bool perform(execution_context & ctx) { + log_verbose(ctx); + ++ctx.m_stats.m_filter_id; if (!ctx.reg(m_reg)) { return true; } @@ -456,7 +497,7 @@ namespace datalog { if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_identical_fn(r, m_cols.size(), m_cols.c_ptr()); if (!fn) { - throw default_exception( + throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_identical operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } @@ -464,12 +505,12 @@ namespace datalog { } (*fn)(r); - if (ctx.eager_emptiness_checking() && r.empty()) { + if (r.fast_empty()) { ctx.make_empty(m_reg); } return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_identical " << m_reg << " "; print_container(m_cols, out); } @@ -493,6 +534,8 @@ namespace datalog { if (!ctx.reg(m_reg)) { return true; } + log_verbose(ctx); + ++ctx.m_stats.m_filter; relation_mutator_fn * fn; relation_base & r = *ctx.reg(m_reg); @@ -500,7 +543,7 @@ namespace datalog { if (!find_fn(r, fn)) { fn = r.get_manager().mk_filter_interpreted_fn(r, m_cond); if (!fn) { - throw default_exception( + throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_interpreted operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } @@ -508,14 +551,14 @@ namespace datalog { } (*fn)(r); - if (ctx.eager_emptiness_checking() && r.empty()) { + if (r.fast_empty()) { ctx.make_empty(m_reg); } - TRACE("dl_verbose", r.display(tout <<"post-filter-interpreted:\n");); + //TRACE("dl_verbose", r.display(tout <<"post-filter-interpreted:\n");); return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_interpreted " << m_reg << " using " << mk_pp(m_cond, m_cond.get_manager()); } @@ -543,10 +586,12 @@ namespace datalog { m_res(result) {} virtual bool perform(execution_context & ctx) { + log_verbose(ctx); if (!ctx.reg(m_src)) { ctx.make_empty(m_res); return true; } + ++ctx.m_stats.m_filter_interp_project; relation_transformer_fn * fn; relation_base & reg = *ctx.reg(m_src); @@ -554,7 +599,7 @@ namespace datalog { if (!find_fn(reg, fn)) { fn = reg.get_manager().mk_filter_interpreted_and_project_fn(reg, m_cond, m_cols.size(), m_cols.c_ptr()); if (!fn) { - throw default_exception( + throw default_exception(default_exception::fmt(), "trying to perform unsupported filter_interpreted_and_project operation on a relation of kind %s", reg.get_plugin().get_name().bare_str()); } @@ -563,14 +608,14 @@ namespace datalog { ctx.set_reg(m_res, (*fn)(reg)); - if (ctx.eager_emptiness_checking() && ctx.reg(m_res)->empty()) { + if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } - TRACE("dl_verbose", reg.display(tout << "post-filter-interpreted-and-project:\n");); + //TRACE("dl_verbose", reg.display(tout << "post-filter-interpreted-and-project:\n");); return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_interpreted_and_project " << m_src << " into " << m_res; out << " using " << mk_pp(m_cond, m_cond.get_manager()); out << " deleting columns "; @@ -606,6 +651,8 @@ namespace datalog { if (!ctx.reg(m_src)) { return true; } + log_verbose(ctx); + ++ctx.m_stats.m_union; relation_base & r_src = *ctx.reg(m_src); if (!ctx.reg(m_tgt)) { relation_base * new_tgt = r_src.get_plugin().mk_empty(r_src); @@ -669,7 +716,7 @@ namespace datalog { r_delta->display(tout <<"delta:"); }); - if (ctx.eager_emptiness_checking() && r_delta && r_delta->empty()) { + if (r_delta && r_delta->fast_empty()) { ctx.make_empty(m_delta); } @@ -685,7 +732,7 @@ namespace datalog { } ctx.set_register_annotation(m_delta, str); } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << (m_widen ? "widen " : "union ") << m_src << " into " << m_tgt; if (m_delta!=execution_context::void_register) { out << " with delta " << m_delta; @@ -713,11 +760,13 @@ namespace datalog { reg_idx tgt) : m_projection(projection), m_src(src), m_cols(col_cnt, cols), m_tgt(tgt) {} virtual bool perform(execution_context & ctx) { - ctx.make_empty(m_tgt); if (!ctx.reg(m_src)) { + ctx.make_empty(m_tgt); return true; } + log_verbose(ctx); + ++ctx.m_stats.m_project_rename; relation_transformer_fn * fn; relation_base & r_src = *ctx.reg(m_src); if (!find_fn(r_src, fn)) { @@ -739,7 +788,7 @@ namespace datalog { return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << (m_projection ? "project " : "rename ") << m_src << " into " << m_tgt; out << (m_projection ? " deleting columns " : " with cycle "); print_container(m_cols, out); @@ -778,17 +827,20 @@ namespace datalog { m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) { } virtual bool perform(execution_context & ctx) { - ctx.make_empty(m_res); + log_verbose(ctx); if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { + ctx.make_empty(m_res); return true; } + ++ctx.m_stats.m_join_project; relation_join_fn * fn; const relation_base & r1 = *ctx.reg(m_rel1); const relation_base & r2 = *ctx.reg(m_rel2); if (!find_fn(r1, r2, fn)) { fn = r1.get_manager().mk_join_project_fn(r1, r2, m_cols1, m_cols2, m_removed_cols); if (!fn) { - throw default_exception("trying to perform unsupported join-project operation on relations of kinds %s and %s", + throw default_exception(default_exception::fmt(), + "trying to perform unsupported join-project operation on relations of kinds %s and %s", r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str()); } store_fn(r1, r2, fn); @@ -796,15 +848,25 @@ namespace datalog { TRACE("dl", tout<\n";); ctx.set_reg(m_res, (*fn)(r1, r2)); TRACE("dl", tout<get_size_estimate_rows()<<"\n";); - if (ctx.eager_emptiness_checking() && ctx.reg(m_res)->empty()) { + if (ctx.reg(m_res)->fast_empty()) { ctx.make_empty(m_res); } return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { - out << "join_project " << m_rel1; + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + relation_base const* r1 = ctx.reg(m_rel1); + relation_base const* r2 = ctx.reg(m_rel2); + out << "join_project " << m_rel1; + if (r1) { + out << ":" << r1->num_columns(); + out << "-" << r1->get_size_estimate_rows(); + } print_container(m_cols1, out); out << " and " << m_rel2; + if (r2) { + out << ":" << r2->num_columns(); + out << "-" << r2->get_size_estimate_rows(); + } print_container(m_cols2, out); out << " into " << m_res << " removing columns "; print_container(m_removed_cols, out); @@ -824,6 +886,59 @@ namespace datalog { removed_cols, result); } + class instr_min : public instruction { + reg_idx m_source_reg; + reg_idx m_target_reg; + unsigned_vector m_group_by_cols; + unsigned m_min_col; + public: + instr_min(reg_idx source_reg, reg_idx target_reg, const unsigned_vector & group_by_cols, unsigned min_col) + : m_source_reg(source_reg), + m_target_reg(target_reg), + m_group_by_cols(group_by_cols), + m_min_col(min_col) { + } + virtual bool perform(execution_context & ctx) { + log_verbose(ctx); + if (!ctx.reg(m_source_reg)) { + ctx.make_empty(m_target_reg); + return true; + } + + const relation_base & s = *ctx.reg(m_source_reg); + if (!s.from_table()) { + throw default_exception(default_exception::fmt(), "relation is not a table %s", + s.get_plugin().get_name().bare_str()); + } + ++ctx.m_stats.m_min; + const table_relation & tr = static_cast(s); + const table_base & source_t = tr.get_table(); + relation_manager & r_manager = s.get_manager(); + + const relation_signature & r_sig = s.get_signature(); + scoped_ptr fn = r_manager.mk_min_fn(source_t, m_group_by_cols, m_min_col); + table_base * target_t = (*fn)(source_t); + + TRACE("dl", + tout << "% "; + target_t->display(tout); + tout << "\n";); + + relation_base * target_r = r_manager.mk_table_relation(r_sig, target_t); + ctx.set_reg(m_target_reg, target_r); + return true; + } + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + out << " MIN AGGR "; + } + virtual void make_annotations(execution_context & ctx) { + } + }; + + instruction * instruction::mk_min(reg_idx source, reg_idx target, const unsigned_vector & group_by_cols, + const unsigned min_col) { + return alloc(instr_min, source, target, group_by_cols, min_col); + } class instr_select_equal_and_project : public instruction { reg_idx m_src; @@ -843,13 +958,14 @@ namespace datalog { ctx.make_empty(m_result); return true; } - + log_verbose(ctx); + ++ctx.m_stats.m_select_equal_project; relation_transformer_fn * fn; relation_base & r = *ctx.reg(m_src); if (!find_fn(r, fn)) { fn = r.get_manager().mk_select_equal_and_project_fn(r, m_value, m_col); if (!fn) { - throw default_exception( + throw default_exception(default_exception::fmt(), "trying to perform unsupported select_equal_and_project operation on a relation of kind %s", r.get_plugin().get_name().bare_str()); } @@ -857,14 +973,14 @@ namespace datalog { } ctx.set_reg(m_result, (*fn)(r)); - if (ctx.eager_emptiness_checking() && ctx.reg(m_result)->empty()) { + if (ctx.reg(m_result)->fast_empty()) { ctx.make_empty(m_result); } return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "select_equal_and_project " << m_src <<" into " << m_result << " col: " << m_col - << " val: " << ctx.get_rmanager().to_nice_string(m_value); + << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); } virtual void make_annotations(execution_context & ctx) { std::stringstream s; @@ -893,9 +1009,12 @@ namespace datalog { const unsigned * cols2) : m_tgt(tgt), m_neg_rel(neg_rel), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2) {} virtual bool perform(execution_context & ctx) { + log_verbose(ctx); if (!ctx.reg(m_tgt) || !ctx.reg(m_neg_rel)) { return true; } + ++ctx.m_stats.m_filter_by_negation; + relation_intersection_filter_fn * fn; relation_base & r1 = *ctx.reg(m_tgt); const relation_base & r2 = *ctx.reg(m_neg_rel); @@ -911,12 +1030,12 @@ namespace datalog { } (*fn)(r1, r2); - if (ctx.eager_emptiness_checking() && r1.empty()) { + if (r1.fast_empty()) { ctx.make_empty(m_tgt); } return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "filter_by_negation on " << m_tgt; print_container(m_cols1, out); out << " with " << m_neg_rel; @@ -948,16 +1067,17 @@ namespace datalog { m_fact.push_back(val); } virtual bool perform(execution_context & ctx) { - ctx.make_empty(m_tgt); + log_verbose(ctx); + ++ctx.m_stats.m_unary_singleton; relation_base * rel = ctx.get_rel_context().get_rmanager().mk_empty_relation(m_sig, m_pred); rel->add_fact(m_fact); ctx.set_reg(m_tgt, rel); return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "mk_unary_singleton into " << m_tgt << " sort:" - << ctx.get_rmanager().to_nice_string(m_sig[0]) << " val:" - << ctx.get_rmanager().to_nice_string(m_sig[0], m_fact[0]); + << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig[0]) << " val:" + << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig[0], m_fact[0]); } virtual void make_annotations(execution_context & ctx) { std::string s; @@ -980,13 +1100,14 @@ namespace datalog { public: instr_mk_total(const relation_signature & sig, func_decl* p, reg_idx tgt) : m_sig(sig), m_pred(p), m_tgt(tgt) {} virtual bool perform(execution_context & ctx) { - ctx.make_empty(m_tgt); + log_verbose(ctx); + ++ctx.m_stats.m_total; ctx.set_reg(m_tgt, ctx.get_rel_context().get_rmanager().mk_full_relation(m_sig, m_pred)); return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "mk_total into " << m_tgt << " sort:" - << ctx.get_rmanager().to_nice_string(m_sig); + << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig); } virtual void make_annotations(execution_context & ctx) { std::string s; @@ -1006,10 +1127,11 @@ namespace datalog { instr_mark_saturated(ast_manager & m, func_decl * pred) : m_pred(pred, m) {} virtual bool perform(execution_context & ctx) { + log_verbose(ctx); ctx.get_rel_context().get_rmanager().mark_saturated(m_pred); return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "mark_saturated " << m_pred->get_name().bare_str(); } virtual void make_annotations(execution_context & ctx) { @@ -1027,12 +1149,13 @@ namespace datalog { instr_assert_signature(const relation_signature & s, reg_idx tgt) : m_sig(s), m_tgt(tgt) {} virtual bool perform(execution_context & ctx) { + log_verbose(ctx); if (ctx.reg(m_tgt)) { SASSERT(ctx.reg(m_tgt)->get_signature()==m_sig); } return true; } - virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { out << "instr_assert_signature of " << m_tgt << " signature:"; print_container(m_sig, out); } @@ -1081,7 +1204,7 @@ namespace datalog { TRACE("dl", tout <<"% "; - instr->display_head_impl(ctx.get_rel_context(), tout); + instr->display_head_impl(ctx, tout); tout <<"\n";); success = !ctx.should_terminate() && instr->perform(ctx); } @@ -1096,6 +1219,15 @@ namespace datalog { } } + + void instruction_block::collect_statistics(statistics& st) const { + instr_seq_type::const_iterator it = m_data.begin(); + instr_seq_type::const_iterator end = m_data.end(); + for(; it!=end; ++it) { + (*it)->collect_statistics(st); + } + } + void instruction_block::make_annotations(execution_context & ctx) { instr_seq_type::iterator it = m_data.begin(); instr_seq_type::iterator end = m_data.end(); @@ -1104,14 +1236,14 @@ namespace datalog { } } - void instruction_block::display_indented(rel_context_base const& _ctx, std::ostream & out, std::string indentation) const { - rel_context const& ctx = dynamic_cast(_ctx); + void instruction_block::display_indented(execution_context const& _ctx, std::ostream & out, std::string indentation) const { + rel_context const& ctx = _ctx.get_rel_context(); instr_seq_type::const_iterator it = m_data.begin(); instr_seq_type::const_iterator end = m_data.end(); for(; it!=end; ++it) { instruction * i = (*it); if (i->passes_output_thresholds(ctx.get_context()) || i->being_recorded()) { - i->display_indented(ctx, out, indentation); + i->display_indented(_ctx, out, indentation); } } } diff --git a/src/muz/rel/dl_instruction.h b/src/muz/rel/dl_instruction.h index fa705a172..160eb7b94 100644 --- a/src/muz/rel/dl_instruction.h +++ b/src/muz/rel/dl_instruction.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_INSTRUCTION_H_ -#define _DL_INSTRUCTION_H_ +#ifndef DL_INSTRUCTION_H_ +#define DL_INSTRUCTION_H_ #include #include @@ -66,14 +66,6 @@ namespace datalog { reg_annotations m_reg_annotation; stopwatch * m_stopwatch; unsigned m_timelimit_ms; //zero means no limit - /** - \brief If true, after every operation that may result in an empty relation, a check - for emptiness will be performed, and if a relation is empty, it will be deleted - and replaced by zero. This allows us to avoid performing operations that would have - no effect due to relation emptiness, but if the check for emptiness is expensive, its - cost may overcome the gains. - */ - bool m_eager_emptiness_checking; public: execution_context(context & context); ~execution_context(); @@ -81,12 +73,33 @@ namespace datalog { void reset(); rel_context & get_rel_context(); + rel_context const & get_rel_context() const; void set_timelimit(unsigned time_in_ms); void reset_timelimit(); bool should_terminate(); - bool eager_emptiness_checking() const { return m_eager_emptiness_checking; } + struct stats { + unsigned m_join; + unsigned m_project; + unsigned m_filter; + unsigned m_total; + unsigned m_unary_singleton; + unsigned m_filter_by_negation; + unsigned m_select_equal_project; + unsigned m_join_project; + unsigned m_project_rename; + unsigned m_union; + unsigned m_filter_interp_project; + unsigned m_filter_id; + unsigned m_filter_eq; + unsigned m_min; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + stats m_stats; + + void collect_statistics(statistics& st) const; /** \brief Return reference to \c i -th register that contains pointer to a relation. @@ -213,7 +226,7 @@ namespace datalog { The newline character at the end should not be printed. */ - virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { + virtual void display_head_impl(execution_context const & ctx, std::ostream & out) const { out << ""; } /** @@ -221,7 +234,9 @@ namespace datalog { Each line must be prepended by \c indentation and ended by a newline character. */ - virtual void display_body_impl(rel_context const & ctx, std::ostream & out, std::string indentation) const {} + virtual void display_body_impl(execution_context const & ctx, std::ostream & out, std::string indentation) const {} + void log_verbose(execution_context& ctx); + public: typedef execution_context::reg_type reg_type; typedef execution_context::reg_idx reg_idx; @@ -232,10 +247,10 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) = 0; - void display(rel_context_base const& ctx, std::ostream & out) const { + void display(execution_context const& ctx, std::ostream & out) const { display_indented(ctx, out, ""); } - void display_indented(rel_context_base const & ctx, std::ostream & out, std::string indentation) const; + void display_indented(execution_context const & ctx, std::ostream & out, std::string indentation) const; static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt); /** @@ -270,6 +285,8 @@ namespace datalog { static instruction * mk_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols, reg_idx result); + static instruction * mk_min(reg_idx source, reg_idx target, const unsigned_vector & group_by_cols, + const unsigned min_col); static instruction * mk_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle, reg_idx tgt); static instruction * mk_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt, @@ -288,6 +305,8 @@ namespace datalog { static instruction * mk_assert_signature(const relation_signature & s, reg_idx tgt); + void collect_statistics(statistics& st) const; + }; @@ -314,7 +333,7 @@ namespace datalog { void push_back(instruction * i) { m_data.push_back(i); - if(m_observer) { + if (m_observer) { m_observer->notify(i); } } @@ -323,6 +342,8 @@ namespace datalog { m_observer = o; } + void collect_statistics(statistics& st) const; + /** \brief Perform instructions in the block. If the run was interrupted before completion, return false; otherwise return true. @@ -336,14 +357,16 @@ namespace datalog { void make_annotations(execution_context & ctx); - void display(rel_context_base const & ctx, std::ostream & out) const { + void display(execution_context const & ctx, std::ostream & out) const { display_indented(ctx, out, ""); } - void display_indented(rel_context_base const & ctx, std::ostream & out, std::string indentation) const; + void display_indented(execution_context const & ctx, std::ostream & out, std::string indentation) const; + + unsigned num_instructions() const { return m_data.size(); } }; }; -#endif /* _DL_INSTRUCTION_H_ */ +#endif /* DL_INSTRUCTION_H_ */ diff --git a/src/muz/rel/dl_interval_relation.cpp b/src/muz/rel/dl_interval_relation.cpp index c04269b02..88c262cbe 100644 --- a/src/muz/rel/dl_interval_relation.cpp +++ b/src/muz/rel/dl_interval_relation.cpp @@ -598,7 +598,7 @@ namespace datalog { // 0 <= y - x - k - 1 if (is_le(to_app(cond->get_arg(0)), x, k, y, is_int) && is_int) { k.neg(); - k -= rational::one(); + k -= rational::one(); std::swap(x, y); return true; } diff --git a/src/muz/rel/dl_interval_relation.h b/src/muz/rel/dl_interval_relation.h index 703cb43c5..a8239ef24 100644 --- a/src/muz/rel/dl_interval_relation.h +++ b/src/muz/rel/dl_interval_relation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_INTERVAL_RELATION_H_ -#define _DL_INTERVAL_RELATION_H_ +#ifndef DL_INTERVAL_RELATION_H_ +#define DL_INTERVAL_RELATION_H_ #include "dl_context.h" diff --git a/src/muz/rel/dl_lazy_table.h b/src/muz/rel/dl_lazy_table.h index 3c0b47367..25870ac1e 100644 --- a/src/muz/rel/dl_lazy_table.h +++ b/src/muz/rel/dl_lazy_table.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef _DL_LAZY_TABLE_H_ -#define _DL_LAZY_TABLE_H_ +#ifndef DL_LAZY_TABLE_H_ +#define DL_LAZY_TABLE_H_ #include "dl_base.h" #include "ref.h" diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp index 7344f187c..7fcbdaaff 100644 --- a/src/muz/rel/dl_mk_explanations.cpp +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -654,7 +654,7 @@ namespace datalog { family_id expl_kind = m_er_plugin->get_kind(); family_id expl_sieve_kind = sieve_plugin.get_relation_kind(sig, expl_sieve, expl_kind); - product_relation_plugin::rel_spec product_spec; + rel_spec product_spec; product_spec.push_back(inner_sieve_kind); product_spec.push_back(expl_sieve_kind); @@ -705,7 +705,7 @@ namespace datalog { rule * mk_explanations::get_e_rule(rule * r) { rule_counter ctr; - ctr.count_rule_vars(m_manager, r); + ctr.count_rule_vars(r); unsigned max_var; unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0; unsigned head_var = next_var++; @@ -833,6 +833,7 @@ namespace datalog { decl_set::iterator end = predicates.end(); for (; it!=end; ++it) { func_decl * orig_decl = *it; + TRACE("dl", tout << mk_pp(orig_decl, m_manager) << "\n";); func_decl * e_decl = get_e_decl(orig_decl); if (!rmgr.try_get_relation(orig_decl) && @@ -854,7 +855,10 @@ namespace datalog { scoped_ptr product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0); SASSERT(product_fun); scoped_rel aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation); + TRACE("dl", tout << aux_extended_rel << " " << aux_extended_rel->get_plugin().get_name() << "\n"; + tout << e_rel.get_plugin().get_name() << "\n";); scoped_ptr union_fun = rmgr.mk_union_fn(e_rel, *aux_extended_rel); + TRACE("dl", tout << union_fun << "\n";); SASSERT(union_fun); (*union_fun)(e_rel, *aux_extended_rel); } diff --git a/src/muz/rel/dl_mk_explanations.h b/src/muz/rel/dl_mk_explanations.h index 9e4d705c3..54e56bbe8 100644 --- a/src/muz/rel/dl_mk_explanations.h +++ b/src/muz/rel/dl_mk_explanations.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _DL_MK_EXPLANATIONS_H_ -#define _DL_MK_EXPLANATIONS_H_ +#ifndef DL_MK_EXPLANATIONS_H_ +#define DL_MK_EXPLANATIONS_H_ #include "dl_context.h" #include "dl_rule_transformer.h" @@ -82,5 +82,5 @@ namespace datalog { }; }; -#endif /* _DL_MK_EXPLANATIONS_H_ */ +#endif /* DL_MK_EXPLANATIONS_H_ */ diff --git a/src/muz/rel/dl_mk_partial_equiv.h b/src/muz/rel/dl_mk_partial_equiv.h index 54a70b3c0..f982952f1 100644 --- a/src/muz/rel/dl_mk_partial_equiv.h +++ b/src/muz/rel/dl_mk_partial_equiv.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ -#define _DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ +#ifndef DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ +#define DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" @@ -45,6 +45,6 @@ namespace datalog { }; -#endif /* _DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */ +#endif /* DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */ diff --git a/src/muz/rel/dl_mk_similarity_compressor.cpp b/src/muz/rel/dl_mk_similarity_compressor.cpp index aa2fe8ab9..75caba6ae 100644 --- a/src/muz/rel/dl_mk_similarity_compressor.cpp +++ b/src/muz/rel/dl_mk_similarity_compressor.cpp @@ -381,7 +381,7 @@ namespace datalog { } rule_counter ctr; - ctr.count_rule_vars(m_manager, r); + ctr.count_rule_vars(r); unsigned max_var_idx, new_var_idx_base; if (ctr.get_max_positive(max_var_idx)) { new_var_idx_base = max_var_idx+1; diff --git a/src/muz/rel/dl_mk_similarity_compressor.h b/src/muz/rel/dl_mk_similarity_compressor.h index 34b76e7e1..9ecbf7bfb 100644 --- a/src/muz/rel/dl_mk_similarity_compressor.h +++ b/src/muz/rel/dl_mk_similarity_compressor.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_SIMILARITY_COMPRESSOR_H_ -#define _DL_MK_SIMILARITY_COMPRESSOR_H_ +#ifndef DL_MK_SIMILARITY_COMPRESSOR_H_ +#define DL_MK_SIMILARITY_COMPRESSOR_H_ #include @@ -74,5 +74,5 @@ namespace datalog { }; -#endif /* _DL_MK_SIMILARITY_COMPRESSOR_H_ */ +#endif /* DL_MK_SIMILARITY_COMPRESSOR_H_ */ diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index c2214dad9..e76b3a25b 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -316,7 +316,7 @@ namespace datalog { void register_rule(rule * r) { rule_counter counter; - counter.count_rule_vars(m, r, 1); + counter.count_rule_vars(r, 1); ptr_vector & rule_content = m_rules_content.insert_if_not_there2(r, ptr_vector())->get_data().m_value; @@ -326,22 +326,22 @@ namespace datalog { for(unsigned i=0; iget_tail(i)); } - for(unsigned i=0; iget_tail(i); var_idx_set t1_vars = rm.collect_vars(t1); - counter.count_vars(m, t1, -1); //temporarily remove t1 variables from counter + counter.count_vars(t1, -1); //temporarily remove t1 variables from counter for(unsigned j=i+1; jget_tail(j); - counter.count_vars(m, t2, -1); //temporarily remove t2 variables from counter + counter.count_vars(t2, -1); //temporarily remove t2 variables from counter var_idx_set scope_vars = rm.collect_vars(t2); scope_vars |= t1_vars; var_idx_set non_local_vars; counter.collect_positive(non_local_vars); - counter.count_vars(m, t2, 1); //restore t2 variables in counter + counter.count_vars(t2, 1); //restore t2 variables in counter set_intersection(non_local_vars, scope_vars); register_pair(t1, t2, r, non_local_vars); } - counter.count_vars(m, t1, 1); //restore t1 variables in counter + counter.count_vars(t1, 1); //restore t1 variables in counter } } @@ -460,16 +460,16 @@ namespace datalog { app * head = r->get_head(); var_counter counter; - counter.count_vars(m, head, 1); + counter.count_vars(head, 1); unsigned tail_size=r->get_tail_size(); unsigned pos_tail_size=r->get_positive_tail_size(); for(unsigned i=pos_tail_size; iget_tail(i), 1); + counter.count_vars(r->get_tail(i), 1); } for(unsigned i=0; i::const_iterator rend = rels.end(); for(; rit!=rend; ++rit) { specs.push_back((*rit)->m_spec); + SASSERT(specs.back().well_formed()); + std::sort(specs.back().begin(), specs.back().end()); } - vector::iterator sit = specs.begin(); - vector::iterator send = specs.end(); - for(; sit!=send; ++sit) { - rel_spec & s = *sit; - std::sort(s.begin(), s.end()); - } + vector::iterator sit = specs.begin(), send = specs.end(); res.reset(); for(;;) { @@ -160,7 +158,7 @@ namespace datalog { sit = specs.begin(); for(; sit!=send; ++sit) { rel_spec & s = *sit; - if(!s.empty() && s.back()==next) { + while (!s.empty() && s.back()==next) { s.pop_back(); } } @@ -184,11 +182,14 @@ namespace datalog { } relation_base * product_relation_plugin::mk_full(func_decl* p, const relation_signature & s, family_id kind) { - if (kind == null_family_id) { - return alloc(product_relation, *this, s); + if (kind == null_family_id || !m_spec_store.contains_signature(s)) { + product_relation* result = alloc(product_relation, *this, s); + result->m_default_empty = false; + return result; } rel_spec spec; m_spec_store.get_relation_spec(s, kind, spec); + SASSERT(spec.well_formed()); relation_vector inner_rels; unsigned rel_cnt = spec.size(); for(unsigned i=0; i intersect_fun = m_rmgr.mk_filter_by_intersection_fn(tgt, src); - if(!intersect_fun) { + if (!intersect_fun) { + TRACE("dl", tgt.display(tout << "tgt\n"); src.display(tout << "src\n");); warning_msg("intersection does not exist"); return; } @@ -611,9 +615,13 @@ namespace datalog { (*union_fun)(tgt, src); } public: - aligned_union_fn(product_relation const& tgt, product_relation const& src, product_relation const* delta, - bool is_widen) : + aligned_union_fn( + product_relation const& tgt, + product_relation const& src, + product_relation const* delta, + bool is_widen) : m_rmgr(tgt.get_manager()), + m_plugin(tgt.get_plugin()), m_is_widen(is_widen) { SASSERT(vectors_equal(tgt.m_spec, src.m_spec)); SASSERT(!delta || vectors_equal(tgt.m_spec, delta->m_spec)); @@ -629,6 +637,9 @@ namespace datalog { virtual void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) { TRACE("dl", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + SASSERT(m_plugin.check_kind(_tgt)); + SASSERT(m_plugin.check_kind(_src)); + SASSERT(!_delta || m_plugin.check_kind(*_delta)); product_relation& tgt = get(_tgt); product_relation const& src = get(_src); product_relation* delta = get(_delta); @@ -650,7 +661,7 @@ namespace datalog { if (i == j) { continue; //this is the basic union which we will perform later } - if (can_do_inner_union(i, j)) { + if (can_do_inner_union(i, j) && can_do_inner_union(j, i)) { TRACE("dl", itgt.display(tout << "tgt:\n"); src[j].display(tout << "src:\n");); // union[i][j] scoped_rel one_side_union = itgt.clone(); @@ -738,7 +749,7 @@ namespace datalog { virtual void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) { - TRACE("dl", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + TRACE("dl_verbose", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); product_relation& tgt = get(_tgt); product_relation const& src0 = get(_src); product_relation* delta = _delta ? get(_delta) : 0; @@ -773,6 +784,7 @@ namespace datalog { m_inner_union_fun(inner_union_fun) {} virtual void operator()(relation_base& tgt, const relation_base& _src, relation_base* delta) { + TRACE("dl", tgt.display(tout); _src.display(tout); ); product_relation const& src = get(_src); (*m_inner_union_fun)(tgt, src[m_single_rel_idx], delta); } @@ -786,7 +798,8 @@ namespace datalog { } return alloc(unaligned_union_fn, get(tgt), get(src), get(delta), is_widen); } - if(check_kind(src)) { + if (check_kind(src)) { + TRACE("dl", tgt.display(tout << "different kinds"); src.display(tout);); const product_relation & p_src = get(src); unsigned single_idx; if(p_src.try_get_single_non_transparent(single_idx)) { @@ -797,7 +810,7 @@ namespace datalog { else { inner = get_manager().mk_union_fn(tgt, p_src[single_idx], delta); } - if(inner) { + if (inner) { return alloc(single_non_transparent_src_union_fn, single_idx, inner); } } @@ -954,20 +967,17 @@ namespace datalog { void product_relation::ensure_correct_kind() { unsigned rel_cnt = m_relations.size(); //the rel_cnt==0 part makes us to update the kind also when the relation is newly created - bool spec_changed = rel_cnt!=m_spec.size() || rel_cnt==0; - if(spec_changed) { + bool spec_changed = rel_cnt != m_spec.size() || rel_cnt==0; + if (spec_changed) { m_spec.resize(rel_cnt); } - for(unsigned i=0;iget_kind(); - if(spec_changed || m_spec[i]!=rkind) { - spec_changed = true; - m_spec[i]=rkind; - } + for (unsigned i = 0; i < rel_cnt; i++) { + family_id rkind = m_relations[i]->get_kind(); + spec_changed |= (m_spec[i] != rkind); + m_spec[i] = rkind; } - if(spec_changed) { - family_id new_kind = get_plugin().get_relation_kind(*this); - set_kind(new_kind); + if (spec_changed) { + set_kind(get_plugin().get_relation_kind(*this)); } } @@ -976,10 +986,20 @@ namespace datalog { func_decl* p = 0; const relation_signature & sig = get_signature(); family_id new_kind = get_plugin().get_relation_kind(sig, spec); - if(new_kind==get_kind()) { + if (new_kind == get_kind()) { return; } + TRACE("dl", { + ast_manager& m = get_ast_manager_from_rel_manager(get_manager()); + sig.output(m, tout); tout << "\n"; + for (unsigned i = 0; i < spec.size(); ++i) { + tout << spec[i] << " "; + } + tout << "\n"; + } + ); + unsigned old_sz = size(); unsigned new_sz = spec.size(); unsigned old_remain = old_sz; @@ -999,13 +1019,13 @@ namespace datalog { } } if(!irel) { - if(old_sz==0 && m_default_empty) { + if(old_sz == 0 && m_default_empty) { //The relation didn't contain any inner relations but it was empty, //so we make the newly added relations empty as well. - irel = get_manager().mk_empty_relation(sig, new_kind); + irel = get_manager().mk_empty_relation(sig, ikind); } else { - irel = get_manager().mk_full_relation(sig, p, new_kind); + irel = get_manager().mk_full_relation(sig, p, ikind); } } new_rels.push_back(irel); @@ -1014,10 +1034,8 @@ namespace datalog { m_relations = new_rels; set_kind(new_kind); - DEBUG_CODE( - ensure_correct_kind(); - SASSERT(get_kind()==new_kind); - ); + m_spec = spec; + SASSERT(get_kind() == new_kind); } bool product_relation::try_get_single_non_transparent(unsigned & idx) const { @@ -1105,7 +1123,11 @@ namespace datalog { } void product_relation::display(std::ostream & out) const { - out<<"Product of the following relations:\n"; + if (m_relations.empty()) { + out << "{}\n"; + return; + } + out << "Product of the following relations:\n"; for (unsigned i = 0; i < m_relations.size(); ++i) { m_relations[i]->display(out); } diff --git a/src/muz/rel/dl_product_relation.h b/src/muz/rel/dl_product_relation.h index 0633ddbf1..6381fcf37 100644 --- a/src/muz/rel/dl_product_relation.h +++ b/src/muz/rel/dl_product_relation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_PRODUCT_RELATION_H_ -#define _DL_PRODUCT_RELATION_H_ +#ifndef DL_PRODUCT_RELATION_H_ +#define DL_PRODUCT_RELATION_H_ #include "dl_context.h" @@ -27,10 +27,12 @@ namespace datalog { class product_relation; + struct rel_spec : public svector { + bool well_formed() const { return true; } + }; + class product_relation_plugin : public relation_plugin { friend class product_relation; - public: - typedef svector rel_spec; private: class join_fn; class transform_fn; @@ -120,8 +122,6 @@ namespace datalog { - typedef product_relation_plugin::rel_spec rel_spec; - /** If m_relations is empty, value of this determines whether the relation is empty or full. */ diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp index 7fcca9ce2..2b78baf05 100644 --- a/src/muz/rel/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -108,7 +108,7 @@ namespace datalog { void relation_manager::store_relation(func_decl * pred, relation_base * rel) { SASSERT(rel); - relation_map::entry * e = m_relations.insert_if_not_there2(pred, 0); + relation_map::obj_map_entry * e = m_relations.insert_if_not_there2(pred, 0); if (e->get_data().m_value) { e->get_data().m_value->deallocate(); } @@ -122,7 +122,7 @@ namespace datalog { relation_map::iterator it = m_relations.begin(); relation_map::iterator end = m_relations.end(); for(; it!=end; ++it) { - if(!it->m_value->empty()) { + if(!it->m_value->fast_empty()) { res.insert(it->m_key); } } @@ -206,6 +206,7 @@ namespace datalog { } void relation_manager::register_relation_plugin_impl(relation_plugin * plugin) { + TRACE("dl", tout << "register: " << plugin->get_name() << "\n";); m_relation_plugins.push_back(plugin); plugin->initialize(get_next_relation_fid(*plugin)); if (plugin->get_name() == get_context().default_relation()) { @@ -349,10 +350,13 @@ namespace datalog { //If there is no plugin to handle the signature, we just create an empty product relation and //stuff will be added to it by later operations. + TRACE("dl", s.output(get_context().get_manager(), tout << "empty product relation"); tout << "\n";); return product_relation_plugin::get_plugin(*this).mk_empty(s); } - + /** + The newly created object takes ownership of the \c table object. + */ relation_base * relation_manager::mk_table_relation(const relation_signature & s, table_base * table) { SASSERT(s.size()==table->get_signature().size()); return get_table_relation_plugin(table->get_plugin()).mk_from_table(s, table); @@ -537,7 +541,7 @@ namespace datalog { for(; it!=end; ++it) { func_decl * pred = *it; relation_base * rel = try_get_relation(pred); - if(!rel) { + if (!rel) { out << "Tuples in " << pred->get_name() << ": \n"; continue; } @@ -668,6 +672,27 @@ namespace datalog { return res; } + class relation_manager::default_relation_apply_sequential_fn : public relation_mutator_fn { + ptr_vector m_mutators; + public: + default_relation_apply_sequential_fn(unsigned n, relation_mutator_fn ** mutators): + m_mutators(n, mutators) { + } + virtual ~default_relation_apply_sequential_fn() { + std::for_each(m_mutators.begin(), m_mutators.end(), delete_proc()); + } + + virtual void operator()(relation_base& t) { + for (unsigned i = 0; i < m_mutators.size(); ++i) { + if (t.empty()) return; + (*(m_mutators[i]))(t); + } + } + }; + + relation_mutator_fn * relation_manager::mk_apply_sequential_fn(unsigned n, relation_mutator_fn ** mutators) { + return alloc(default_relation_apply_sequential_fn, n, mutators); + } class relation_manager::default_relation_join_project_fn : public relation_join_fn { scoped_ptr m_join; @@ -733,8 +758,7 @@ namespace datalog { relation_union_fn * relation_manager::mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta) { - TRACE("dl", tout << src.get_plugin().get_name() << " " << tgt.get_plugin().get_name() << "\n";); + const relation_base * delta) { relation_union_fn * res = tgt.get_plugin().mk_union_fn(tgt, src, delta); if(!res && &tgt.get_plugin()!=&src.get_plugin()) { res = src.get_plugin().mk_union_fn(tgt, src, delta); @@ -742,6 +766,7 @@ namespace datalog { if(!res && delta && &tgt.get_plugin()!=&delta->get_plugin() && &src.get_plugin()!=&delta->get_plugin()) { res = delta->get_plugin().mk_union_fn(tgt, src, delta); } + // TRACE("dl", tout << src.get_plugin().get_name() << " " << tgt.get_plugin().get_name() << " " << (res?"created":"not created") << "\n";); return res; } @@ -998,6 +1023,11 @@ namespace datalog { return res; } + table_min_fn * relation_manager::mk_min_fn(const table_base & t, + unsigned_vector & group_by_cols, const unsigned col) + { + return t.get_plugin().mk_min_fn(t, group_by_cols, col); + } class relation_manager::auxiliary_table_transformer_fn { table_fact m_row; @@ -1385,7 +1415,7 @@ namespace datalog { dl_decl_util & m_decl_util; th_rewriter & m_simp; app_ref m_condition; - ptr_vector m_var_sorts; + expr_free_vars m_free_vars; expr_ref_vector m_args; public: default_table_filter_interpreted_fn(context & ctx, unsigned col_cnt, app* condition) @@ -1395,8 +1425,7 @@ namespace datalog { m_simp(ctx.get_rewriter()), m_condition(condition, ctx.get_manager()), m_args(ctx.get_manager()) { - m_var_sorts.resize(col_cnt); - get_free_vars(m_condition, m_var_sorts); + m_free_vars(m_condition); } virtual bool should_remove(const table_fact & f) const { @@ -1406,14 +1435,13 @@ namespace datalog { //arguments need to be in reverse order for the substitution unsigned col_cnt = f.size(); for(int i=col_cnt-1;i>=0;i--) { - sort * var_sort = m_var_sorts[i]; - if(!var_sort) { + if(!m_free_vars.contains(i)) { args.push_back(0); continue; //this variable does not occur in the condition; } table_element el = f[i]; - args.push_back(m_decl_util.mk_numeral(el, var_sort)); + args.push_back(m_decl_util.mk_numeral(el, m_free_vars[i])); } expr_ref ground(m_ast_manager); diff --git a/src/muz/rel/dl_relation_manager.h b/src/muz/rel/dl_relation_manager.h index 2c148c5e6..cda6373d3 100644 --- a/src/muz/rel/dl_relation_manager.h +++ b/src/muz/rel/dl_relation_manager.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_RELATION_MANAGER_H_ -#define _DL_RELATION_MANAGER_H_ +#ifndef DL_RELATION_MANAGER_H_ +#define DL_RELATION_MANAGER_H_ #include"map.h" @@ -43,6 +43,7 @@ namespace datalog { class default_relation_select_equal_and_project_fn; class default_relation_intersection_filter_fn; class default_relation_filter_interpreted_and_project_fn; + class default_relation_apply_sequential_fn; class auxiliary_table_transformer_fn; class auxiliary_table_filter_fn; @@ -72,7 +73,7 @@ namespace datalog { typedef map, ptr_eq > rp2fprp_map; - typedef map, ptr_eq > relation_map; + typedef obj_map relation_map; typedef ptr_vector table_plugin_vector; typedef ptr_vector relation_plugin_vector; @@ -175,6 +176,7 @@ namespace datalog { table_plugin * get_table_plugin(symbol const& s); relation_plugin * get_relation_plugin(symbol const& s); relation_plugin & get_relation_plugin(family_id kind); + void set_favourite_plugin(relation_plugin* p) { m_favourite_relation_plugin = p; } table_relation_plugin & get_table_relation_plugin(table_plugin & tp); bool try_get_finite_product_relation_plugin(const relation_plugin & inner, finite_product_relation_plugin * & res); @@ -249,6 +251,9 @@ namespace datalog { return mk_join_fn(t1, t2, cols1.size(), cols1.c_ptr(), cols2.c_ptr(), allow_product_relation); } + table_min_fn * mk_min_fn(const table_base & t, + unsigned_vector & group_by_cols, const unsigned col); + /** \brief Return functor that transforms a table into one that lacks columns listed in \c removed_cols array. @@ -352,9 +357,13 @@ namespace datalog { relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); + relation_mutator_fn * mk_apply_sequential_fn(unsigned n, relation_mutator_fn* * mutators); + + /** \brief Operations that returns all rows of \c t for which is column \c col equal to \c value with the column \c col removed. @@ -697,5 +706,5 @@ namespace datalog { }; -#endif /* _DL_RELATION_MANAGER_H_ */ +#endif /* DL_RELATION_MANAGER_H_ */ diff --git a/src/muz/rel/dl_sieve_relation.h b/src/muz/rel/dl_sieve_relation.h index 48402cd6d..da6c9bbff 100644 --- a/src/muz/rel/dl_sieve_relation.h +++ b/src/muz/rel/dl_sieve_relation.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _DL_SIEVE_RELATION_H_ -#define _DL_SIEVE_RELATION_H_ +#ifndef DL_SIEVE_RELATION_H_ +#define DL_SIEVE_RELATION_H_ #include "dl_context.h" #include "dl_relation_manager.h" @@ -194,5 +194,5 @@ namespace datalog { }; -#endif /* _DL_SIEVE_RELATION_H_ */ +#endif /* DL_SIEVE_RELATION_H_ */ diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp index 3b2dc333a..2c806971f 100644 --- a/src/muz/rel/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -509,7 +509,7 @@ namespace datalog { } bool sparse_table::add_fact(const char * data) { - verbose_action _va("add_fact", 3); + verbose_action _va("add_fact", 10); m_data.write_into_reserve(data); return add_reserve_content(); } @@ -829,7 +829,6 @@ namespace datalog { virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { - verbose_action _va("join_project"); const sparse_table & t1 = get(tb1); const sparse_table & t2 = get(tb2); diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h index 0222aa6e2..95dccdf89 100644 --- a/src/muz/rel/dl_sparse_table.h +++ b/src/muz/rel/dl_sparse_table.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _DL_SPARSE_TABLE_H_ -#define _DL_SPARSE_TABLE_H_ +#ifndef DL_SPARSE_TABLE_H_ +#define DL_SPARSE_TABLE_H_ #include #include @@ -495,4 +495,4 @@ namespace datalog { }; - #endif /* _DL_SPARSE_TABLE_H_ */ +#endif /* DL_SPARSE_TABLE_H_ */ diff --git a/src/muz/rel/dl_table.h b/src/muz/rel/dl_table.h index 3a240c337..b77e860d6 100644 --- a/src/muz/rel/dl_table.h +++ b/src/muz/rel/dl_table.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_TABLE_H_ -#define _DL_TABLE_H_ +#ifndef DL_TABLE_H_ +#define DL_TABLE_H_ #include #include @@ -261,5 +261,5 @@ namespace datalog { }; -#endif /* _DL_TABLE_H_ */ +#endif /* DL_TABLE_H_ */ diff --git a/src/muz/rel/dl_table_plugin.h b/src/muz/rel/dl_table_plugin.h index 134389b61..7765f54d3 100644 --- a/src/muz/rel/dl_table_plugin.h +++ b/src/muz/rel/dl_table_plugin.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_TABLE_PLUGIN_H_ -#define _DL_TABLE_PLUGIN_H_ +#ifndef DL_TABLE_PLUGIN_H_ +#define DL_TABLE_PLUGIN_H_ #include"ast.h" #include"map.h" @@ -189,5 +189,5 @@ namespace datalog { }; -#endif /* _DL_TABLE_PLUGIN_H_ */ +#endif /* DL_TABLE_PLUGIN_H_ */ diff --git a/src/muz/rel/dl_table_relation.cpp b/src/muz/rel/dl_table_relation.cpp index 364c29367..d42d071aa 100644 --- a/src/muz/rel/dl_table_relation.cpp +++ b/src/muz/rel/dl_table_relation.cpp @@ -63,6 +63,9 @@ namespace datalog { return alloc(table_relation, *this, s, t); } + /** + The newly created object takes ownership of the \c t object. + */ relation_base * table_relation_plugin::mk_from_table(const relation_signature & s, table_base * t) { if (&t->get_plugin() == &m_table_plugin) return alloc(table_relation, *this, s, t); diff --git a/src/muz/rel/dl_table_relation.h b/src/muz/rel/dl_table_relation.h index f86143c0f..8266995da 100644 --- a/src/muz/rel/dl_table_relation.h +++ b/src/muz/rel/dl_table_relation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_TABLE_RELATION_H_ -#define _DL_TABLE_RELATION_H_ +#ifndef DL_TABLE_RELATION_H_ +#define DL_TABLE_RELATION_H_ #include "dl_base.h" @@ -129,5 +129,5 @@ namespace datalog { }; -#endif /* _DL_TABLE_RELATION_H_ */ +#endif /* DL_TABLE_RELATION_H_ */ diff --git a/src/muz/rel/dl_vector_relation.h b/src/muz/rel/dl_vector_relation.h index 4d0917359..040d23300 100644 --- a/src/muz/rel/dl_vector_relation.h +++ b/src/muz/rel/dl_vector_relation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_VECTOR_RELATION_H_ -#define _DL_VECTOR_RELATION_H_ +#ifndef DL_VECTOR_RELATION_H_ +#define DL_VECTOR_RELATION_H_ #include "ast_pp.h" #include "dl_context.h" @@ -42,8 +42,6 @@ namespace datalog { union_find_default_ctx m_ctx; union_find<>* m_eqs; - friend class vector_relation_plugin; - public: vector_relation(relation_plugin& p, relation_signature const& s, bool is_empty, T const& t = T()): relation_base(p, s), @@ -105,9 +103,10 @@ namespace datalog { display_index(i, (*m_elems)[i], out); } else { - out << i << " = " << find(i) << "\n"; + out << i << " = " << find(i) << " "; } } + out << "\n"; } diff --git a/src/muz/rel/doc.cpp b/src/muz/rel/doc.cpp new file mode 100644 index 000000000..e73395bc9 --- /dev/null +++ b/src/muz/rel/doc.cpp @@ -0,0 +1,739 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + doc.cpp + +Abstract: + + difference of cubes. + +Author: + + Nuno Lopes (a-nlopes) 2013-03-01 + Nikolaj Bjorner (nbjorner) 2014-09-15 + +Revision History: + + Based on ternary_diff_bitvector by Nuno Lopes. + +--*/ + +#include "doc.h" +#include "smt_kernel.h" +#include "expr_safe_replace.h" +#include "smt_params.h" +#include "ast_util.h" +#include "ast_pp.h" + +doc_manager::doc_manager(unsigned n): m(n), m_alloc("doc") { + m_full = m.allocateX(); +} + +doc_manager::~doc_manager() { + m.deallocate(m_full); +} + +doc* doc_manager::allocate() { + return allocate(m.allocate()); +} +doc* doc_manager::allocate1() { + return allocate(m.allocate1()); +} +doc* doc_manager::allocate0() { + return allocate(m.allocate0()); +} +doc* doc_manager::allocateX() { + return allocate(m.allocateX()); +} +doc* doc_manager::allocate(doc const& src) { + doc* r = allocate(m.allocate(src.pos())); + for (unsigned i = 0; i < src.neg().size(); ++i) { + r->neg().push_back(m.allocate(src.neg()[i])); + } + return r; +} +doc* doc_manager::allocate(tbv* t) { + SASSERT(t); + void* mm = m_alloc.allocate(sizeof(doc)); + return new (mm) doc(t); +} +doc* doc_manager::allocate(tbv const& src) { + return allocate(m.allocate(src)); +} +doc* doc_manager::allocate(uint64 n) { + return allocate(m.allocate(n)); +} +doc* doc_manager::allocate(rational const& r) { + return allocate(m.allocate(r)); +} +doc* doc_manager::allocate(uint64 n, unsigned hi, unsigned lo) { + return allocate(m.allocate(n, hi, lo)); +} +doc* doc_manager::allocate(doc const& src, unsigned const* permutation) { + doc* r = allocate(m.allocate(src.pos(), permutation)); + for (unsigned i = 0; i < src.neg().size(); ++i) { + r->neg().push_back(m.allocate(src.neg()[i], permutation)); + } + return r; +} +void doc_manager::deallocate(doc* src) { + if (!src) return; + m.deallocate(&src->pos()); + src->neg().reset(m); + src->~doc(); + m_alloc.deallocate(sizeof(doc), src); +} +void doc_manager::copy(doc& dst, doc const& src) { + m.copy(dst.pos(), src.pos()); + dst.neg().reset(m); + for (unsigned i = 0; i < src.neg().size(); ++i) { + dst.neg().push_back(m.allocate(src.neg()[i])); + } +} +doc& doc_manager::fill0(doc& src) { + src.neg().reset(m); + m.fill0(src.pos()); + return src; +} +doc& doc_manager::fill1(doc& src) { + src.neg().reset(m); + m.fill1(src.pos()); + return src; +} +doc& doc_manager::fillX(doc& src) { + src.neg().reset(m); + m.fillX(src.pos()); + return src; +} + +unsigned doc_manager::get_size_estimate_bytes(const doc& d) const { + return m.get_size_estimate_bytes(d.pos()) + + d.neg().get_size_estimate_bytes(m) + + sizeof(doc); +} + +bool doc_manager::set_and(doc& dst, doc const& src) { + // (A \ B) & (C \ D) = (A & C) \ (B u D) + if (!m.set_and(dst.pos(), src.pos())) return false; + dst.neg().intersect(m, dst.pos()); + tbv_ref t(m); + for (unsigned i = 0; i < src.neg().size(); ++i) { + t = m.allocate(src.neg()[i]); + if (m.set_and(*t, dst.pos())) { + dst.neg().insert(m, t.detach()); + } + } + return fold_neg(dst); +} +bool doc_manager::set_and(doc& dst, tbv const& src) { + // (A \ B) & C = (A & C) \ (B & C) + if (!m.set_and(dst.pos(), src)) return false; + dst.neg().intersect(m, src); + return fold_neg(dst); +} + +bool doc_manager::well_formed(doc const& d) const { + if (!m.is_well_formed(d.pos())) return false; + for (unsigned i = 0; i < d.neg().size(); ++i) { + if (!m.is_well_formed(d.neg()[i])) return false; + if (!m.contains(d.pos(), d.neg()[i])) return false; + } + return true; +} + +bool doc_manager::fold_neg(doc& dst) { + start_over: + for (unsigned i = 0; i < dst.neg().size(); ++i) { + if (m.contains(dst.neg()[i], dst.pos())) + return false; + + unsigned index; + unsigned count = diff_by_012(dst.pos(), dst.neg()[i], index); + if (count != 2) { + if (count == 0) { + return false; + } + else if (count == 3) { + dst.neg().erase(tbvm(), i); + --i; + } + else { // count == 1: + m.set(dst.pos(), index, neg(dst.neg()[i][index])); + dst.neg().intersect(tbvm(), dst.pos()); + goto start_over; + } + } + } + SASSERT(well_formed(dst)); + return true; +} + +unsigned doc_manager::diff_by_012(tbv const& pos, tbv const& neg, unsigned& index) { + unsigned n = num_tbits(); + unsigned count = 0; + for (unsigned i = 0; i < n; ++i) { + tbit b1 = pos[i]; + tbit b2 = neg[i]; + SASSERT(b1 != BIT_z && b2 != BIT_z); + if (b1 != b2) { + if (count == 1) return 2; + if (b1 == BIT_x) { + index = i; + count = 1; + } + else if (b2 != BIT_x) { + return 3; + } + } + } + return count; +} + +void doc_manager::set(doc& d, unsigned idx, tbit value) { + m.set(d.pos(), idx, value); + for (unsigned i = 0; i < d.neg().size(); ++i) { + tbit b = d.neg()[i][idx]; + if (b != BIT_x && value != BIT_x && value != b) { + d.neg().erase(tbvm(), i); + --i; + } + else { + m.set(d.neg()[i], idx, value); + } + } +} + +// +// merge range from [lo:lo+length-1] with each index in equivalence class. +// under assumption of equalities and columns that are discarded. +// +bool doc_manager::merge( + doc& d, unsigned lo, unsigned length, + subset_ints const& equalities, bit_vector const& discard_cols) { + for (unsigned i = 0; i < length; ++i) { + unsigned idx = lo + i; + if (!merge(d, idx, equalities, discard_cols)) return false; + } + return true; +} +bool doc_manager::merge(doc& d, unsigned idx, subset_ints const& equalities, + bit_vector const& discard_cols) { + unsigned root = equalities.find(idx); + idx = root; + unsigned num_x = 0; + unsigned root1 = root; + tbit value = BIT_x; + do { + switch (d[idx]) { + case BIT_0: + if (value == BIT_1) return false; + value = BIT_0; + break; + case BIT_1: + if (value == BIT_0) return false; + value = BIT_1; + break; + case BIT_x: + ++num_x; + if (!discard_cols.get(idx)) { + root1 = idx; + } + break; + default: + UNREACHABLE(); + break; + } + idx = equalities.next(idx); + } + while (idx != root); + + TRACE("doc", tout << "num_x: " << num_x << " value: " << value << "\n";); + if (num_x == 0) { + // nothing to do. + } + else if (value != BIT_x) { + do { + if (d[idx] == BIT_x) { + set(d, idx, value); + } + idx = equalities.next(idx); + } + while (idx != root); + } + else { + bool all_x = true; + if (!d.neg().is_empty()) { + idx = root; + do { + for (unsigned i = 0; all_x && i < d.neg().size(); ++i) { + all_x = (BIT_x == d.neg()[i][idx]); + } + idx = equalities.next(idx); + } + while (idx != root && all_x); + } + idx = root; + do { + if ((!discard_cols.get(idx) || !all_x) && idx != root1) { + tbv* t = m.allocate(d.pos()); + m.set(*t, idx, BIT_0); + m.set(*t, root1, BIT_1); + d.neg().insert(tbvm(), t); + t = m.allocate(d.pos()); + m.set(*t, idx, BIT_1); + m.set(*t, root1, BIT_0); + d.neg().insert(tbvm(), t); + } + idx = equalities.next(idx); + } + while (idx != root); + } + return true; +} + +bool doc_manager::intersect(doc const& A, doc const& B, doc& result) { + copy(result, A); + return set_and(result, B); +} + +// +// 1. If n = 0,1: can project directly. +// 2. If tbv_i uses X in all positions with vars or constant where tbv is constant: can project directly. +// 3. Perform resolution on remaining tbv_i +// +// tbv & ~tbv1 & ~tbv2 & .. & ~tbv_n +// Semantics of ~tbv1 is that it is a clause of literals. +// indices where BIT_1 is set are negative. +// indices where BIT_0 is set are positive. +// + +doc* doc_manager::project(doc_manager& dstm, bit_vector const& to_delete, doc const& src) { + tbv_manager& dstt = dstm.m; + tbv_ref t(dstt); + t = dstt.project(to_delete, src.pos()); + doc* r = dstm.allocate(t.detach()); + SASSERT(r); + + if (src.neg().is_empty()) { + return r; + } + + // + // A negation can be projected directly if it does not constrain + // deleted variables. + // + tbv_vector todo, new_todo; + for (unsigned i = 0; i < src.neg().size(); ++i) { + todo.push_back(tbvm().allocate(src.neg()[i])); + } + unsigned idx; + bool done = false; + while (!todo.empty() && !done) { + switch(pick_resolvent(src.pos(), todo, to_delete, idx)) { + case project_is_empty: + t = dstt.allocate(r->pos()); + r->neg().push_back(t.detach()); + done = true; + break; + case project_monolithic: + done = true; + break; + case project_neg: + case project_pos: + for (unsigned i = 0; i < todo.size(); ++i) { + tbv& tx = *todo[i]; + if (tx[idx] == BIT_x) { + new_todo.push_back(&tx); + } + else { + m.deallocate(&tx); + } + } + std::swap(new_todo, todo); + new_todo.reset(); + break; + case project_resolve: { + utbv pos, neg; + for (unsigned i = 0; i < todo.size(); ++i) { + tbv& tx = *todo[i]; + switch(tx[idx]) { + case BIT_x: new_todo.push_back(&tx); break; + case BIT_0: neg.push_back(&tx); break; + case BIT_1: pos.push_back(&tx); break; + default: UNREACHABLE(); break; + } + } + TRACE("doc", + tout << "pos: "; + for (unsigned i = 0; i < pos.size(); ++i) { + tbvm().display(tout, pos[i]) << " "; + } + tout << "\nneg: "; + for (unsigned i = 0; i < neg.size(); ++i) { + tbvm().display(tout, neg[i]) << " "; + } + tout << "\n"; + ); + SASSERT(pos.size() > 0 && neg.size() > 0); + tbv_ref t1(m); + for (unsigned j = 0; j < pos.size(); ++j) { + for (unsigned k = 0; k < neg.size(); ++k) { + t1 = m.allocate(pos[j]); + m.set(*t1, idx, BIT_x); + if (tbvm().set_and(*t1, neg[k])) { + m.set(*t1, idx, BIT_x); + new_todo.push_back(t1.detach()); + } + } + } + pos.reset(m); + neg.reset(m); + std::swap(todo, new_todo); + new_todo.reset(); + break; + } + case project_done: { + for (unsigned i = 0; i < todo.size(); ++i) { + t = dstt.project(to_delete, *todo[i]); + if (dstt.equals(r->pos(), *t)) { + r->neg().reset(dstt); + r->neg().push_back(t.detach()); + break; + } + if (r->neg().size() > 0 && dstt.equals(r->neg()[0], *t)) { + continue; + } + r->neg().push_back(t.detach()); + } + done = true; + break; + } + } + } + for (unsigned i = 0; i < todo.size(); ++i) { + m.deallocate(todo[i]); + } + return r; +} + +doc* doc_manager::join(const doc& d1, const doc& d2, doc_manager& dm1, + const unsigned_vector& cols1, + const unsigned_vector& cols2) { + doc_ref d(*this, allocateX()); + tbv_ref t(m); + tbv& pos = d->pos(); + utbv& neg = d->neg(); + unsigned mid = dm1.num_tbits(); + unsigned hi = num_tbits(); + m.set(pos, d1.pos(), mid - 1, 0); + m.set(pos, d2.pos(), hi - 1, mid); + SASSERT(well_formed(*d)); + + // first fix bits + for (unsigned i = 0; i < cols1.size(); ++i) { + unsigned idx1 = cols1[i]; + unsigned idx2 = mid + cols2[i]; + tbit v1 = pos[idx1]; + tbit v2 = pos[idx2]; + + if (v1 == BIT_x) { + if (v2 != BIT_x) + m.set(pos, idx1, v2); + } + else if (v2 == BIT_x) { + m.set(pos, idx2, v1); + } + else if (v1 != v2) { + // columns don't match + return 0; + } + SASSERT(well_formed(*d)); + } + + // fix equality of don't care columns + for (unsigned i = 0; i < cols1.size(); ++i) { + unsigned idx1 = cols1[i]; + unsigned idx2 = mid + cols2[i]; + unsigned v1 = pos[idx1]; + unsigned v2 = pos[idx2]; + + if (v1 == BIT_x && v2 == BIT_x) { + // add to subtracted TBVs: 1xx0 and 0xx1 + t = m.allocate(pos); + m.set(*t, idx1, BIT_0); + m.set(*t, idx2, BIT_1); + neg.push_back(t.detach()); + t = m.allocate(pos); + m.set(*t, idx1, BIT_1); + m.set(*t, idx2, BIT_0); + neg.push_back(t.detach()); + } + SASSERT(well_formed(*d)); + } + + // handle subtracted TBVs: 1010 -> 1010xxx + for (unsigned i = 0; i < d1.neg().size(); ++i) { + t = m.allocateX(); + m.set(*t, d1.neg()[i], mid - 1, 0); + if (m.set_and(*t, pos)) + neg.push_back(t.detach()); + SASSERT(well_formed(*d)); + } + for (unsigned i = 0; i < d2.neg().size(); ++i) { + t = m.allocateX(); + m.set(*t, d2.neg()[i], hi - 1, mid); + if (m.set_and(*t, pos)) + neg.push_back(t.detach()); + SASSERT(well_formed(*d)); + } + SASSERT(well_formed(*d)); + + return d.detach(); +} + +doc_manager::project_action_t +doc_manager::pick_resolvent( + tbv const& pos, tbv_vector const& neg, bit_vector const& to_delete, unsigned& idx) { + if (neg.empty()) return project_done; + for (unsigned j = 0; j < neg.size(); ++j) { + if (m.equals(pos, *neg[j])) return project_is_empty; + } + + unsigned best_pos = UINT_MAX; + unsigned best_neg = UINT_MAX; + unsigned best_idx = UINT_MAX; + for (unsigned i = 0; i < num_tbits(); ++i) { + if (!to_delete.get(i)) continue; + if (pos[i] != BIT_x) continue; + unsigned num_pos = 0, num_neg = 0; + tbit b1 = (*neg[0])[i]; + if (b1 == BIT_0) num_neg++; + if (b1 == BIT_1) num_pos++; + bool monolithic = true; + for (unsigned j = 1; j < neg.size(); ++j) { + tbit b2 = (*neg[j])[i]; + if (b1 != b2) { + monolithic = false; + } + if (b2 == BIT_0) num_neg++; + if (b2 == BIT_1) num_pos++; + } + if (monolithic && b1 != BIT_x) { + idx = i; + return project_monolithic; + } + if (monolithic && b1 == BIT_x) { + continue; + } + SASSERT(!monolithic); + if (num_pos == 0) { + SASSERT(num_neg > 0); + idx = i; + return project_neg; + } + if (num_neg == 0) { + SASSERT(num_pos > 0); + idx = i; + return project_pos; + } + if ((best_pos >= num_pos && best_neg >= num_neg) || + num_neg == 1 || num_pos == 1) { + best_pos = num_pos; + best_neg = num_neg; + best_idx = i; + } + } + if (best_idx != UINT_MAX) { + idx = best_idx; + return project_resolve; + } + else { + return project_done; + } +} + + + +void doc_manager::complement(doc const& src, doc_vector& result) { + result.reset(); + if (is_full(src)) { + return; + } + doc* r = allocateX(); + r->neg().push_back(m.allocate(src.pos())); + result.push_back(r); + for (unsigned i = 0; i < src.neg().size(); ++i) { + result.push_back(allocate(src.neg()[i])); + } +} +// (A \ {A1}) \ (B \ {B1}) +// (A & !A1 & & !B) | (A & B1 & !A1) +// A \ {A1 u B} u (A & B1) \ {A1} +void doc_manager::subtract(doc const& A, doc const& B, doc_vector& result) { + doc_ref r(*this); + tbv_ref t(m); + r = allocate(A); + t = m.allocate(B.pos()); + if (m.set_and(*t, A.pos())) { + r->neg().insert(m, t.detach()); + } + if (fold_neg(*r)) + result.push_back(r.detach()); + + for (unsigned i = 0; i < B.neg().size(); ++i) { + r = allocate(A); + if (set_and(*r, B.neg()[i])) { + result.push_back(r.detach()); + } + } +} +bool doc_manager::equals(doc const& a, doc const& b) const { + if (!m.equals(a.pos(), b.pos())) return false; + if (a.neg().size() != b.neg().size()) return false; + for (unsigned i = 0; i < a.neg().size(); ++i) { + if (!m.equals(a.neg()[i], b.neg()[i])) return false; + } + return true; +} +bool doc_manager::is_full(doc const& src) const { + return src.neg().is_empty() && m.equals(src.pos(), *m_full); +} +bool doc_manager::is_empty_complete(ast_manager& m, doc const& src) { + if (src.neg().size() == 0) return false; + + smt_params fp; + smt::kernel s(m, fp); + expr_ref fml = to_formula(m, src); + s.assert_expr(fml); + lbool res = s.check(); + if (res == l_true) { + return false; + } + SASSERT(res == l_false); + return true; +} + +unsigned doc_manager::hash(doc const& src) const { + unsigned r = 0; + for (unsigned i = 0; i < src.neg().size(); ++i) { + r = 2*r + m.hash(src.neg()[i]); + } + return r + m.hash(src.pos()); +} +// approximation +// A \ (A1 u A2) contains B \ (B1 u B2) +// if +// A contains B +// B1 contains A1 or B2 contains A1 +// B1 contains A2 or B2 contains A2 +bool doc_manager::contains(doc const& a, doc const& b) const { + if (!m.contains(a.pos(), b.pos())) return false; + for (unsigned i = 0; i < a.neg().size(); ++i) { + bool found = false; + for (unsigned j = 0; !found && j < b.neg().size(); ++j) { + found = m.contains(b.neg()[j],a.neg()[i]); + } + if (!found) return false; + } + return true; +} + +bool doc_manager::contains(doc const& a, unsigned_vector const& colsa, + doc const& b, unsigned_vector const& colsb) const { + if (!m.contains(a.pos(), colsa, b.pos(), colsb)) + return false; + + for (unsigned i = 0; i < a.neg().size(); ++i) { + bool found = false; + for (unsigned j = 0; !found && j < b.neg().size(); ++j) { + found = m.contains(b.neg()[j], colsb, a.neg()[i], colsa); + } + if (!found) return false; + } + return true; +} + +std::ostream& doc_manager::display(std::ostream& out, doc const& b) const { + if (num_tbits() == 0) return out << "[]"; + return display(out, b, num_tbits()-1, 0); +} + +std::ostream& doc_manager::display(std::ostream& out, doc const& b, unsigned hi, unsigned lo) const { + m.display(out, b.pos(), hi, lo); + if (b.neg().is_empty()) return out; + out << " \\ "; + b.neg().display(m, out, hi, lo); + return out; +} + + +void doc_manager::verify_project(ast_manager& m, doc_manager& dstm, bit_vector const& to_delete, doc const& src, doc const& dst) { + expr_ref fml1 = to_formula(m, src); + expr_ref fml2 = dstm.to_formula(m, dst); + project_rename(fml2, to_delete); + project_expand(fml1, to_delete); + check_equiv(m, fml1, fml2); +} + +void doc_manager::check_equiv(ast_manager& m, expr* fml1, expr* fml2) { + smt_params fp; + smt::kernel solver(m, fp); + expr_ref fml(m); + fml = m.mk_not(m.mk_eq(fml1, fml2)); + solver.assert_expr(fml); + lbool res = solver.check(); + if (res != l_false) { + TRACE("doc", + tout << mk_pp(fml1, m) << "\n"; + tout << mk_pp(fml2, m) << "\n"; + ); + UNREACHABLE(); + throw 0; + } + SASSERT(res == l_false); +} + +expr_ref doc_manager::to_formula(ast_manager& m, doc const& src) { + expr_ref result(m); + expr_ref_vector conj(m); + conj.push_back(tbvm().to_formula(m, src.pos())); + for (unsigned i = 0; i < src.neg().size(); ++i) { + conj.push_back(m.mk_not(tbvm().to_formula(m, src.neg()[i]))); + } + result = mk_and(m, conj.size(), conj.c_ptr()); + return result; +} + +void doc_manager::project_expand(expr_ref& fml, bit_vector const& to_delete) { + ast_manager& m = fml.get_manager(); + expr_ref tmp1(m), tmp2(m); + for (unsigned i = 0; i < num_tbits(); ++i) { + if (to_delete.get(i)) { + expr_safe_replace rep1(m), rep2(m); + rep1.insert(tbvm().mk_var(m, i), m.mk_true()); + rep1(fml, tmp1); + rep2.insert(tbvm().mk_var(m, i), m.mk_false()); + rep2(fml, tmp2); + if (tmp1 == tmp2) { + fml = tmp1; + } + else { + fml = m.mk_or(tmp1, tmp2); + } + } + } +} + +void doc_manager::project_rename(expr_ref& fml, bit_vector const& to_delete) { + ast_manager& m = fml.get_manager(); + expr_safe_replace rep(m); + for (unsigned i = 0, j = 0; i < num_tbits(); ++i) { + if (!to_delete.get(i)) { + rep.insert(tbvm().mk_var(m, j), tbvm().mk_var(m, i)); + ++j; + } + } + rep(fml); +} diff --git a/src/muz/rel/doc.h b/src/muz/rel/doc.h new file mode 100644 index 000000000..23b636cf9 --- /dev/null +++ b/src/muz/rel/doc.h @@ -0,0 +1,393 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + doc.h + +Abstract: + + difference of cubes. + +Author: + + Nuno Lopes (a-nlopes) 2013-03-01 + Nikolaj Bjorner (nbjorner) 2014-09-15 + +Revision History: + + Based on ternary_diff_bitvector by Nuno Lopes. + +--*/ + +#ifndef DOC_H_ +#define DOC_H_ + +#include "tbv.h" +#include "union_find.h" +#include "buffer.h" + + +class doc; +template class union_bvec; +typedef union_find<> subset_ints; +typedef union_bvec utbv; +typedef buffer tbv_vector; +typedef buffer doc_vector; + +class doc_manager { + tbv_manager m; + tbv* m_full; + small_object_allocator m_alloc; + enum project_action_t { + project_is_empty, + project_done, + project_monolithic, + project_neg, + project_pos, + project_resolve + }; + project_action_t pick_resolvent( + tbv const& pos, tbv_vector const& neg, bit_vector const& to_delete, unsigned& idx); +public: + doc_manager(unsigned num_bits); + ~doc_manager(); + tbv_manager& tbvm() { return m; } + tbv_manager const& tbvm() const { return m; } + doc* allocate(); + doc* allocate1(); + doc* allocate0(); + doc* allocateX(); + doc* allocate(doc const& src); + doc* allocate(tbv const& src); + doc* allocate(tbv * src); + doc* allocate(uint64 n); + doc* allocate(rational const& r); + doc* allocate(uint64 n, unsigned hi, unsigned lo); + doc* allocate(doc const& src, unsigned const* permutation); + void deallocate(doc* src); + void copy(doc& dst, doc const& src); + doc& reset(doc& src) { return fill0(src); } + doc& fill0(doc& src); + doc& fill1(doc& src); + doc& fillX(doc& src); + bool is_full(doc const& src) const; + bool is_empty_complete(ast_manager& m, doc const& src); + bool set_and(doc& dst, doc const& src); + bool set_and(doc& dst, tbv const& src); + bool fold_neg(doc& dst); + bool intersect(doc const& A, doc const& B, doc& result); + void complement(doc const& src, doc_vector& result); + void subtract(doc const& A, doc const& B, doc_vector& result); + bool equals(doc const& a, doc const& b) const; + unsigned hash(doc const& src) const; + bool contains(doc const& a, doc const& b) const; + bool contains(doc const& a, unsigned_vector const& colsa, + doc const& b, unsigned_vector const& colsb) const; + std::ostream& display(std::ostream& out, doc const& b) const; + std::ostream& display(std::ostream& out, doc const& b, unsigned hi, unsigned lo) const; + unsigned num_tbits() const { return m.num_tbits(); } + unsigned get_size_estimate_bytes(const doc& d) const; + doc* project(doc_manager& dstm, bit_vector const& to_delete, doc const& src); + bool well_formed(doc const& d) const; + bool merge(doc& d, unsigned lo, unsigned length, subset_ints const& equalities, bit_vector const& discard_cols); + void set(doc& d, unsigned idx, tbit value); + doc* join(const doc& a, const doc& b, doc_manager& dm1, + const unsigned_vector& cols1, const unsigned_vector& cols2); + + void verify_project(ast_manager& m, doc_manager& dstm, bit_vector const& to_delete, doc const& src, doc const& dst); +private: + unsigned diff_by_012(tbv const& pos, tbv const& neg, unsigned& index); + bool merge(doc& d, unsigned idx, subset_ints const& equalities, bit_vector const& discard_cols); + void project_rename(expr_ref& fml, bit_vector const& to_delete); + void project_expand(expr_ref& fml, bit_vector const& to_delete); + expr_ref to_formula(ast_manager& m, doc const& src); + void check_equiv(ast_manager& m, expr* fml1, expr* fml2); +}; + + +// union of tbv*, union of doc* +template +class union_bvec { + buffer m_elems; // TBD: reuse allocator of M + + enum fix_bit_result_t { + e_row_removed, // = 1 + e_duplicate_row, // = 2 + e_fixed + }; + + +public: + unsigned size() const { return m_elems.size(); } + T& operator[](unsigned idx) const { return *m_elems[idx]; } + bool is_empty() const { return m_elems.empty(); } + bool is_empty_complete(ast_manager& m, doc_manager& dm) const { + for (unsigned i = 0; i < size(); ++i) { + if (!dm.is_empty_complete(m, *m_elems[i])) + return false; + } + return true; + } + bool is_full(M& m) const { return size() == 1 && m.is_full(*m_elems[0]); } + bool contains(M& m, T& t) const { + for (unsigned i = 0; i < size(); ++i) { + if (m.contains(*m_elems[i], t)) return true; + } + return false; + } + std::ostream& display(M const& m, std::ostream& out) const { + if (m.num_tbits() == 0) return out << "[]"; + return display(m, out, m.num_tbits()-1, 0); + } + std::ostream& display(M const& m, std::ostream& out, unsigned hi, unsigned lo) const { + out << "{"; + if (size() + m.num_tbits() > 10) out << "\n "; + for (unsigned i = 0; i < size(); ++i) { + m.display(out, *m_elems[i], hi, lo); + if (i + 1 < size()) out << ", "; + if (i + 1 < size() && m.num_tbits() > 10) out << "\n "; + } + return out << "}"; + } + + void push_back(T* t) { + SASSERT(t); + m_elems.push_back(t); + } + void erase(M& m, unsigned idx) { + m.deallocate(m_elems[idx]); + unsigned sz = m_elems.size(); + for (unsigned i = idx+1; i < sz; ++i) { + m_elems[i-1] = m_elems[i]; + } + m_elems.resize(sz-1); + } + void reset(M& m) { + for (unsigned i = 0; i < m_elems.size(); ++i) { + m.deallocate(m_elems[i]); + } + m_elems.reset(); + } + bool insert(M& m, T* t) { + SASSERT(t); + unsigned sz = size(), j = 0; + bool found = false; + unsigned i = 0; + for ( ; i < sz; ++i, ++j) { + if (m.contains(*m_elems[i], *t)) { + found = true; + } + else if (m.contains(*t, *m_elems[i])) { + m.deallocate(m_elems[i]); + --j; + continue; + } + if (i != j) { + m_elems[j] = m_elems[i]; + } + } + if (j != sz) m_elems.resize(j); + if (found) { + m.deallocate(t); + } + else { + m_elems.push_back(t); + } + return !found; + } + void intersect(M& m, T const& t) { + unsigned sz = size(); + unsigned j = 0; + for (unsigned i = 0; i < sz; ++i, ++j) { + if (!m.set_and(*m_elems[i], t)) { + m.deallocate(m_elems[i]); + --j; + } + else if (i != j) { + m_elems[j] = m_elems[i]; + } + } + if (j != sz) m_elems.resize(j); + } + void insert(M& m, union_bvec const& other) { + for (unsigned i = 0; i < other.size(); ++i) { + insert(m, &other[i]); + } + } + void intersect(M& m, union_bvec const& other) { + unsigned sz = other.size(); + union_bvec tmp, result; + for (unsigned i = 0; i < sz; ++i) { + tmp.copy(m, *this); + tmp.intersect(m, other[i]); + for (unsigned j = 0; j < tmp.size(); ++j) { + result.push_back(tmp.m_elems[j]); + } + tmp.m_elems.reset(); + } + std::swap(*this, result); + result.reset(m); + } + void subtract(M& m, union_bvec const& other) { + unsigned sz = other.size(); + for (unsigned i = 0; !is_empty() && i < sz; ++i) { + subtract(m, other[i]); + } + // TBD compress? + } + void subtract(M& m, T& t) { + unsigned sz = size(); + union_bvec result; + for (unsigned i = 0; i < sz; ++i) { + m.subtract(*m_elems[i], t, result.m_elems); + } + std::swap(m_elems, result.m_elems); + result.reset(m); + } + void complement(M& m, union_bvec& result) const { + union_bvec negated; + result.reset(m); + result.push_back(m.allocateX()); + unsigned sz = size(); + for (unsigned i = 0; !is_empty() && i < sz; ++i) { + m.complement(*m_elems[i], negated.m_elems); + result.intersect(m, negated); + negated.reset(m); + } + } + void copy(M& m, union_bvec const& other) { + reset(m); + for (unsigned i = 0; i < other.size(); ++i) { + push_back(m.allocate(other[i])); + } + } + void simplify(M& m) { + union_bvec result; + for (unsigned i = 0; i < size(); ++i) { + if (m.fold_neg(*m_elems[i])) { + result.insert(m, m_elems[i]); + } + else { + m.deallocate(m_elems[i]); + } + } + std::swap(*this, result); + } + + bool well_formed(M& m) const { + for (unsigned i = 0; i < size(); ++i) { + if (!m.well_formed(*m_elems[i])) return false; + } + return true; + } + + void merge(M& m, unsigned lo, unsigned length, subset_ints const& equalities, bit_vector const& discard_cols) { + unsigned j = 0; + unsigned sz = size(); + for (unsigned i = 0; i < sz; ++i, ++j) { + if (!m.merge(*m_elems[i], lo, length, equalities, discard_cols)) { + --j; + m.deallocate(m_elems[i]); + } + else if (i != j) { + m_elems[j] = m_elems[i]; + } + } + if (j != sz) m_elems.resize(j); + } + + void merge(M& m, unsigned lo1, unsigned lo2, unsigned length, bit_vector const& discard_cols) { + union_find_default_ctx union_ctx; + subset_ints equalities(union_ctx); + for (unsigned i = 0; i < discard_cols.size(); ++i) { + equalities.mk_var(); + } + for (unsigned j = 0; j < length; ++j) { + equalities.merge(lo1 + j, lo2 + j); + } + merge(m, lo1, length, equalities, discard_cols); + } + + void merge(M& m, unsigned_vector const& roots, subset_ints const& equalities, + bit_vector const& discard_cols) { + for (unsigned i = 0; i < roots.size(); ++i) { + merge(m, roots[i], 1, equalities, discard_cols); + } + } + + void join(const union_bvec& d1, const union_bvec& d2, M& dm, M& dm1, + const unsigned_vector& cols1, const unsigned_vector& cols2) { + for (unsigned i = 0; i < d1.size(); ++i) { + for (unsigned j = 0; j < d2.size(); ++j) { + if (T *d = dm.join(d1[i], d2[j], dm1, cols1, cols2)) + insert(dm, d); + } + } + } + + unsigned get_size_estimate_bytes(const M& m) const { + unsigned sz = sizeof(T*) * size(); + for (unsigned i = 0; i < size(); ++i) { + sz += m.get_size_estimate_bytes(*m_elems[i]); + } + return sz; + } +}; + + +class doc { + // pos \ (neg_0 \/ ... \/ neg_n) + friend class doc_manager; + tbv* m_pos; + utbv m_neg; +public: + + struct eq { + doc_manager& m; + eq(doc_manager& m):m(m) {} + bool operator()(doc const& d1, doc const& d2) const { + return m.equals(d1, d2); + } + }; + + struct hash { + doc_manager& m; + hash(doc_manager& m):m(m) {} + unsigned operator()(doc const& d) const { + return m.hash(d); + } + }; + + doc(tbv* t): m_pos(t) {} + tbv& pos() { return *m_pos; } + utbv& neg() { return m_neg; } + tbv const& pos() const { return *m_pos; } + utbv const& neg() const { return m_neg; } + tbit operator[](unsigned idx) const { return pos()[idx]; } +}; + +typedef union_bvec udoc; + +class doc_ref { + doc_manager& dm; + doc* d; +public: + doc_ref(doc_manager& dm):dm(dm),d(0) {} + doc_ref(doc_manager& dm, doc* d):dm(dm),d(d) {} + ~doc_ref() { + if (d) dm.deallocate(d); + } + doc_ref& operator=(doc* d2) { + if (d) dm.deallocate(d); + d = d2; + return *this; + } + doc& operator*() { return *d; } + doc* operator->() { return d; } + doc* detach() { doc* r = d; d = 0; return r; } + operator bool() const { return d != 0; } +}; + +#endif /* DOC_H_ */ + diff --git a/src/muz/rel/karr_relation.cpp b/src/muz/rel/karr_relation.cpp index 436cd8598..d7ea7de6b 100644 --- a/src/muz/rel/karr_relation.cpp +++ b/src/muz/rel/karr_relation.cpp @@ -1,5 +1,12 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "karr_relation.h" #include "bool_rewriter.h" +#include "ast_util.h" namespace datalog { class karr_relation : public relation_base { @@ -108,7 +115,7 @@ namespace datalog { var* v, *w; rational n1, n2; expr_ref_vector conjs(m); - qe::flatten_and(cond, conjs); + flatten_and(cond, conjs); matrix& M = get_ineqs(); unsigned num_columns = get_signature().size(); diff --git a/src/muz/rel/karr_relation.h b/src/muz/rel/karr_relation.h index 72448f026..90c9abbd4 100644 --- a/src/muz/rel/karr_relation.h +++ b/src/muz/rel/karr_relation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _KARR_RELATION_H_ -#define _KARR_RELATION_H_ +#ifndef KARR_RELATION_H_ +#define KARR_RELATION_H_ #include"dl_mk_karr_invariants.h" #include"dl_relation_manager.h" @@ -84,5 +84,5 @@ namespace datalog { }; -#endif /* _DL_MK_KARR_INVARIANTS_H_ */ +#endif /* DL_MK_KARR_INVARIANTS_H_ */ diff --git a/src/muz/rel/rel_context.cpp b/src/muz/rel/rel_context.cpp index 4e219b2f7..c6b47f1ed 100644 --- a/src/muz/rel/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -21,6 +21,7 @@ Revision History: #include"rel_context.h" +#include"stopwatch.h" #include"dl_context.h" #include"dl_compiler.h" #include"dl_instruction.h" @@ -31,6 +32,8 @@ Revision History: #include"dl_interval_relation.h" #include"karr_relation.h" #include"dl_finite_product_relation.h" +#include"udoc_relation.h" +#include"check_relation.h" #include"dl_lazy_table.h" #include"dl_sparse_table.h" #include"dl_table.h" @@ -47,7 +50,7 @@ Revision History: #include"dl_mk_interp_tail_simplifier.h" #include"dl_mk_bit_blast.h" #include"dl_mk_separate_negated_tails.h" -#include"fixedpoint_params.hpp" +#include"ast_util.h" namespace datalog { @@ -95,7 +98,8 @@ namespace datalog { m_rmanager(ctx), m_answer(m), m_last_result_relation(0), - m_ectx(ctx) { + m_ectx(ctx), + m_sw(0) { // register plugins for builtin tables @@ -111,7 +115,9 @@ namespace datalog { rm.register_plugin(alloc(bound_relation_plugin, rm)); rm.register_plugin(alloc(interval_relation_plugin, rm)); - rm.register_plugin(alloc(karr_relation_plugin, rm)); + if (m_context.karr()) rm.register_plugin(alloc(karr_relation_plugin, rm)); + rm.register_plugin(alloc(udoc_plugin, rm)); + rm.register_plugin(alloc(check_relation_plugin, rm)); } rel_context::~rel_context() { @@ -128,9 +134,9 @@ namespace datalog { lbool rel_context::saturate(scoped_query& sq) { m_context.ensure_closed(); - bool time_limit = m_context.timeout()!=0; - unsigned remaining_time_limit = m_context.timeout(); + unsigned remaining_time_limit = m_context.soft_timeout(); unsigned restart_time = m_context.initial_restart_timeout(); + bool time_limit = remaining_time_limit != 0; instruction_block termination_code; @@ -149,18 +155,20 @@ namespace datalog { break; } TRACE("dl", m_context.display(tout);); + //IF_VERBOSE(3, m_context.display_smt2(0,0,verbose_stream());); - if (m_context.get_params().dump_aig().size()) { - const char *filename = static_cast(m_context.get_params().dump_aig().c_ptr()); + if (m_context.print_aig().size()) { + const char *filename = static_cast(m_context.print_aig().c_ptr()); aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts); std::ofstream strm(filename, std::ios_base::binary); aig(strm); exit(0); } - compiler::compile(m_context, m_context.get_rules(), m_code, termination_code); + ::stopwatch sw; + sw.start(); - TRACE("dl", m_code.display(*this, tout); ); + compiler::compile(m_context, m_context.get_rules(), m_code, termination_code); bool timeout_after_this_round = time_limit && (restart_time==0 || remaining_time_limit<=restart_time); @@ -176,6 +184,9 @@ namespace datalog { VERIFY( termination_code.perform(m_ectx) || m_context.canceled()); m_code.process_all_costs(); + sw.stop(); + m_sw += sw.get_seconds(); + IF_VERBOSE(10, m_ectx.report_big_relations(1000, verbose_stream());); @@ -218,6 +229,7 @@ namespace datalog { } lbool rel_context::query(unsigned num_rels, func_decl * const* rels) { + setup_default_relation(); get_rmanager().reset_saturated_marks(); scoped_query _scoped_query(m_context); for (unsigned i = 0; i < num_rels; ++i) { @@ -229,12 +241,13 @@ namespace datalog { switch(res) { case l_true: { + const rule_set& rules = m_context.get_rules(); expr_ref_vector ans(m); expr_ref e(m); bool some_non_empty = num_rels == 0; bool is_approx = false; for (unsigned i = 0; i < num_rels; ++i) { - func_decl* q = m_context.get_rules().get_pred(rels[i]); + func_decl* q = rules.get_pred(rels[i]); relation_base& rel = get_relation(q); if (!rel.empty()) { some_non_empty = true; @@ -243,11 +256,21 @@ namespace datalog { is_approx = true; } rel.to_formula(e); +#if 0 + // Alternative format: + // List the signature of the relation as + // part of the answer. + expr_ref_vector args(m); + for (unsigned j = 0; j < q->get_arity(); ++j) { + args.push_back(m.mk_var(j, q->get_domain(j))); + } + e = m.mk_implies(m.mk_app(q, args.size(), args.c_ptr()), e); +#endif ans.push_back(e); } SASSERT(!m_last_result_relation); if (some_non_empty) { - m_answer = m.mk_and(ans.size(), ans.c_ptr()); + m_answer = mk_and(m, ans.size(), ans.c_ptr()); if (is_approx) { res = l_undef; m_context.set_status(APPROX); @@ -268,23 +291,31 @@ namespace datalog { return res; } +#define _MIN_DONE_ 1 + void rel_context::transform_rules() { rule_transformer transf(m_context); +#ifdef _MIN_DONE_ transf.register_plugin(alloc(mk_coi_filter, m_context)); +#endif transf.register_plugin(alloc(mk_filter_rules, m_context)); transf.register_plugin(alloc(mk_simple_joins, m_context)); if (m_context.unbound_compressor()) { transf.register_plugin(alloc(mk_unbound_compressor, m_context)); } +#ifdef _MIN_DONE_ if (m_context.similarity_compressor()) { transf.register_plugin(alloc(mk_similarity_compressor, m_context)); } +#endif transf.register_plugin(alloc(mk_partial_equivalence_transformer, m_context)); +#ifdef _MIN_DONE_ transf.register_plugin(alloc(mk_rule_inliner, m_context)); +#endif transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context)); transf.register_plugin(alloc(mk_separate_negated_tails, m_context)); - if (m_context.get_params().bit_blast()) { + if (m_context.xform_bit_blast()) { transf.register_plugin(alloc(mk_bit_blast, m_context, 22000)); transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context, 21000)); } @@ -303,6 +334,7 @@ namespace datalog { } lbool rel_context::query(expr* query) { + setup_default_relation(); get_rmanager().reset_saturated_marks(); scoped_query _scoped_query(m_context); rule_manager& rm = m_context.get_rule_manager(); @@ -352,14 +384,15 @@ namespace datalog { } void rel_context::reset_negated_tables() { - rule_set::pred_set_vector const & pred_sets = m_context.get_rules().get_strats(); + const rule_set& all_rules = m_context.get_rules(); + rule_set::pred_set_vector const & pred_sets = all_rules.get_strats(); bool non_empty = false; for (unsigned i = 1; i < pred_sets.size(); ++i) { func_decl_set::iterator it = pred_sets[i]->begin(), end = pred_sets[i]->end(); for (; it != end; ++it) { func_decl* pred = *it; relation_base & rel = get_relation(pred); - if (!rel.empty()) { + if (!rel.fast_empty()) { non_empty = true; break; } @@ -380,7 +413,7 @@ namespace datalog { if (depends_on_negation.contains(pred)) { continue; } - rule_vector const& rules = m_context.get_rules().get_predicate_rules(pred); + rule_vector const& rules = all_rules.get_predicate_rules(pred); bool inserted = false; for (unsigned j = 0; !inserted && j < rules.size(); ++j) { rule* r = rules[j]; @@ -433,7 +466,7 @@ namespace datalog { bool rel_context::is_empty_relation(func_decl* pred) const { relation_base* rb = try_get_relation(pred); - return !rb || rb->empty(); + return !rb || rb->fast_empty(); } relation_manager & rel_context::get_rmanager() { return m_rmanager; } @@ -463,7 +496,7 @@ namespace datalog { target_kind = get_ordinary_relation_plugin(relation_names[0]).get_kind(); break; default: { - svector rel_kinds; // kinds of plugins that are not table plugins + rel_spec rel_kinds; // kinds of plugins that are not table plugins family_id rel_kind; // the aggregate kind of non-table plugins for (unsigned i = 0; i < relation_name_cnt; i++) { relation_plugin & p = get_ordinary_relation_plugin(relation_names[i]); @@ -491,6 +524,12 @@ namespace datalog { get_rmanager().set_cancel(f); } + void rel_context::setup_default_relation() { + if (m_context.default_relation() == symbol("doc")) { + m_context.set_unbound_compressor(false); + } + } + relation_plugin & rel_context::get_ordinary_relation_plugin(symbol relation_name) { relation_plugin * plugin = get_rmanager().get_relation_plugin(relation_name); if (!plugin) { @@ -514,27 +553,13 @@ namespace datalog { SASSERT(m_last_result_relation); return m_last_result_relation->contains_fact(f); } - - void rel_context::reset_tables() { - get_rmanager().reset_saturated_marks(); - rule_set::decl2rules::iterator it = m_context.get_rules().begin_grouped_rules(); - rule_set::decl2rules::iterator end = m_context.get_rules().end_grouped_rules(); - for (; it != end; ++it) { - func_decl* p = it->m_key; - relation_base & rel = get_relation(p); - rel.reset(); - } - for (unsigned i = 0; i < m_table_facts.size(); ++i) { - func_decl* pred = m_table_facts[i].first; - relation_fact const& fact = m_table_facts[i].second; - get_relation(pred).add_fact(fact); - } - } void rel_context::add_fact(func_decl* pred, relation_fact const& fact) { get_rmanager().reset_saturated_marks(); get_relation(pred).add_fact(fact); - m_table_facts.push_back(std::make_pair(pred, fact)); + if (m_context.print_aig().size()) { + m_table_facts.push_back(std::make_pair(pred, fact)); + } } void rel_context::add_fact(func_decl* pred, table_fact const& fact) { @@ -563,6 +588,31 @@ namespace datalog { get_rmanager().store_relation(pred, rel); } + void rel_context::collect_statistics(statistics& st) const { + st.update("saturation time", m_sw); + m_code.collect_statistics(st); + m_ectx.collect_statistics(st); + } + + void rel_context::updt_params() { + if (m_context.check_relation() != symbol::null && + m_context.check_relation() != symbol("null")) { + symbol cr("check_relation"); + m_context.set_default_relation(cr); + relation_plugin* p = get_rmanager().get_relation_plugin(cr); + SASSERT(p); + check_relation_plugin* p1 = dynamic_cast(p); + relation_plugin* p2 = get_rmanager().get_relation_plugin(m_context.check_relation()); + SASSERT(p2); + SASSERT(p1 != p2); + p1->set_plugin(p2); + get_rmanager().set_favourite_plugin(p1); + if (m_context.check_relation() == symbol("doc")) { + m_context.set_unbound_compressor(false); + } + } + } + void rel_context::inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) { if (orig_pred) { family_id target_kind = get_rmanager().get_requested_predicate_kind(orig_pred); @@ -584,11 +634,6 @@ namespace datalog { m_code.make_annotations(m_ectx); m_code.process_all_costs(); - out << "\n--------------\n"; - out << "Instructions\n"; - m_code.display(*this, out); - - out << "\n--------------\n"; out << "Big relations\n"; m_ectx.report_big_relations(1000, out); diff --git a/src/muz/rel/rel_context.h b/src/muz/rel/rel_context.h index 68a6fbd90..5d85a1e7c 100644 --- a/src/muz/rel/rel_context.h +++ b/src/muz/rel/rel_context.h @@ -18,8 +18,8 @@ Revision History: Extracted from dl_context --*/ -#ifndef _REL_CONTEXT_H_ -#define _REL_CONTEXT_H_ +#ifndef REL_CONTEXT_H_ +#define REL_CONTEXT_H_ #include "ast.h" #include "dl_relation_manager.h" #include "dl_instruction.h" @@ -41,6 +41,7 @@ namespace datalog { fact_vector m_table_facts; execution_context m_ectx; instruction_block m_code; + double m_sw; class scoped_query; @@ -48,12 +49,12 @@ namespace datalog { relation_plugin & get_ordinary_relation_plugin(symbol relation_name); - void reset_tables(); - lbool saturate(scoped_query& sq); void set_cancel(bool f); + void setup_default_relation(); + public: rel_context(context& ctx); @@ -79,9 +80,11 @@ namespace datalog { virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred); + virtual void collect_statistics(statistics& st) const; virtual void cancel() { set_cancel(true); } virtual void cleanup() { set_cancel(false);} + virtual void updt_params(); /** \brief Restrict the set of used predicates to \c res. @@ -127,4 +130,4 @@ namespace datalog { }; }; -#endif /* _REL_CONTEXT_H_ */ +#endif /* REL_CONTEXT_H_ */ diff --git a/src/muz/rel/tbv.cpp b/src/muz/rel/tbv.cpp new file mode 100644 index 000000000..6e030a823 --- /dev/null +++ b/src/muz/rel/tbv.cpp @@ -0,0 +1,328 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + tbv.cpp + +Abstract: + + ternary bit-vector utilities. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-09-15 + +Revision History: + + +--*/ + +#include "tbv.h" +#include "hashtable.h" +#include "ast_util.h" + + +static bool s_debug_alloc = false; + +void tbv_manager::debug_alloc() { + s_debug_alloc = true; +} + +tbv_manager::~tbv_manager() { + DEBUG_CODE( + ptr_vector::iterator it = allocated_tbvs.begin(); + ptr_vector::iterator end = allocated_tbvs.end(); + for (; it != end; ++it) { + std::cout << "dangling: " << (*it) << "\n"; + TRACE("doc", tout << "dangling: " << (*it) << "\n";); + }); +} + +void tbv_manager::reset() { + m.reset(); +} +tbv* tbv_manager::allocate() { + tbv* r = reinterpret_cast(m.allocate()); + DEBUG_CODE( + if (s_debug_alloc) { + TRACE("doc", tout << allocated_tbvs.size() << " " << r << "\n";); + } + allocated_tbvs.insert(r); + ); + return r; +} +tbv* tbv_manager::allocate1() { + tbv* v = allocate(); + fill1(*v); + return v; +} +tbv* tbv_manager::allocate0() { + tbv* v = allocate(); + fill0(*v); + return v; +} +tbv* tbv_manager::allocateX() { + tbv* v = allocate(); + fillX(*v); + return v; +} +tbv* tbv_manager::allocate(tbv const& bv) { + tbv* r = allocate(); + copy(*r, bv); + return r; +} +tbv* tbv_manager::allocate(uint64 val) { + tbv* v = allocate0(); + for (unsigned bit = num_tbits(); bit > 0;) { + --bit; + if (val & (1ULL << bit)) { + set(*v, bit, BIT_1); + } else { + set(*v, bit, BIT_0); + } + } + return v; +} + +tbv* tbv_manager::allocate(uint64 val, unsigned hi, unsigned lo) { + tbv* v = allocateX(); + SASSERT(64 >= num_tbits() && num_tbits() > hi && hi >= lo); + set(*v, val, hi, lo); + return v; +} +tbv* tbv_manager::allocate(tbv const& bv, unsigned const* permutation) { + tbv* r = allocate(); + unsigned sz = num_tbits(); + for (unsigned i = 0; i < sz; ++i) { + set(*r, permutation[i], bv[i]); + } + return r; +} +tbv* tbv_manager::allocate(char const* bv) { + tbv* result = allocateX(); + unsigned i = 0, sz = num_tbits(); + while(*bv && i < sz) { + if (*bv == '0') set(*result, i++, BIT_0); + else if (*bv == '1') set(*result, i++, BIT_1); + else if (*bv == '*') i++; + else if (*bv == 'x') i++; + else if (i == 0 && (*bv == ' ' || *bv == '\t')) ; + else break; + ++bv; + } + return result; +} + +tbv* tbv_manager::project(bit_vector const& to_delete, tbv const& src) { + tbv* r = allocate(); + unsigned i, j; + unsigned n = to_delete.size(); + for (i = 0, j = 0; i < n; ++i) { + if (!to_delete.get(i)) { + set(*r, j, src[i]); + ++j; + } + } + SASSERT(num_tbits() == j); + return r; +} + +void tbv_manager::set(tbv& dst, unsigned index, tbit value) { + SASSERT(index < num_tbits()); + m.set(dst, 2*index, (value & 2) != 0); + m.set(dst, 2*index+1, (value & 1) != 0); +} + + +void tbv_manager::set(tbv& dst, uint64 val, unsigned hi, unsigned lo) { + SASSERT(lo <= hi && hi < num_tbits()); + for (unsigned i = 0; i < hi - lo + 1; ++i) { + set(dst, lo + i, (val & (1ULL << i))?BIT_1:BIT_0); + } +} +void tbv_manager::set(tbv& dst, rational const& r, unsigned hi, unsigned lo) { + SASSERT(lo <= hi && hi < num_tbits()); + if (r.is_uint64()) { + set(dst, r.get_uint64(), hi, lo); + return; + } + for (unsigned i = 0; i < hi - lo + 1; ++i) { + if (bitwise_and(r, rational::power_of_two(i)).is_zero()) + set(dst, lo + i, BIT_0); + else + set(dst, lo + i, BIT_1); + } +} + +void tbv_manager::set(tbv& dst, tbv const& other, unsigned hi, unsigned lo) { + dst.set(other, 2*hi+1, 2*lo); +} + + +tbv* tbv_manager::allocate(rational const& r) { + if (r.is_uint64()) { + return allocate(r.get_uint64()); + } + tbv* v = allocate0(); + for (unsigned bit = num_tbits(); bit > 0; ) { + --bit; + if (bitwise_and(r, rational::power_of_two(bit)).is_zero()) { + set(*v, bit, BIT_0); + } else { + set(*v, bit, BIT_1); + } + } + return v; +} +void tbv_manager::deallocate(tbv* bv) { + DEBUG_CODE( + if (!allocated_tbvs.contains(bv)) { + std::cout << "double deallocate: " << bv << "\n"; + UNREACHABLE(); + } + if (s_debug_alloc) { + TRACE("doc", tout << "deallocate: " << bv << "\n";); + } + allocated_tbvs.erase(bv);); + m.deallocate(bv); +} +void tbv_manager::copy(tbv& dst, tbv const& src) const { + m.copy(dst, src); +} +tbv& tbv_manager::fill0(tbv& bv) const { + // 10101010 = 2 + 8 + 32 + 128 + memset(bv.m_data, 2 + 8 + 32 + 128, m.num_bytes()); + return bv; +} +tbv& tbv_manager::fill1(tbv& bv) const { + // 01010101 = 1 + 4 + 16 + 64 + memset(bv.m_data, 1 + 4 + 16 + 64, m.num_bytes()); + return bv; +} +tbv& tbv_manager::fillX(tbv& bv) const { + m.fill1(bv); + return bv; +} + +tbv& tbv_manager::set_or(tbv& dst, tbv const& src) const { + m.set_or(dst, src); + return dst; +} +bool tbv_manager::set_and(tbv& dst, tbv const& src) const { + m.set_and(dst, src); + return is_well_formed(dst); +} + +bool tbv_manager::is_well_formed(tbv const& dst) const { + unsigned nw = m.num_words(); + unsigned w; + for (unsigned i = 0; i + 1 < nw; ++i) { + w = dst.get_word(i); + w = w | (w << 1) | 0x55555555; + if (w != 0xFFFFFFFF) return false; + } + if (nw > 0) { + w = m.last_word(dst); + w = w | (w << 1) | 0x55555555 | ~m.get_mask(); + if (w != 0xFFFFFFFF) return false; + } + return true; +} + +void tbv_manager::complement(tbv const& src, ptr_vector& result) { + tbv* r; + unsigned n = num_tbits(); + for (unsigned i = 0; i < n; ++i) { + switch (src.get(i)) { + case BIT_0: + r = allocate(src); + set(*r, i, BIT_1); + result.push_back(r); + break; + case BIT_1: + r = allocate(src); + set(*r, i, BIT_0); + result.push_back(r); + break; + default: + break; + } + } +} + +bool tbv_manager::equals(tbv const& a, tbv const& b) const { + return m.equals(a, b); +} +unsigned tbv_manager::hash(tbv const& src) const { + return m.hash(src); +} +bool tbv_manager::contains(tbv const& a, tbv const& b) const { + return m.contains(a, b); +} + +bool tbv_manager::contains(tbv const& a, unsigned_vector const& colsa, + tbv const& b, unsigned_vector const& colsb) const { + for (unsigned i = 0; i < colsa.size(); ++i) { + tbit bit_a = a[colsa[i]]; + if (bit_a == BIT_x) continue; + if (bit_a != b[colsb[i]]) return false; + } + return true; +} + +bool tbv_manager::intersect(tbv const& a, tbv const& b, tbv& result) { + copy(result, a); + return set_and(result, b); +} + +std::ostream& tbv_manager::display(std::ostream& out, tbv const& b, unsigned hi, unsigned lo) const { + SASSERT(lo <= hi && hi < num_tbits()); + for (unsigned i = hi+1; i-- > lo; ) { + switch (b.get(i)) { + case BIT_0: + out << '0'; + break; + case BIT_1: + out << '1'; + break; + case BIT_x: + out << 'x'; + break; + case BIT_z: + out << 'z'; + break; + default: + UNREACHABLE(); + } + } + return out; +} + +std::ostream& tbv_manager::display(std::ostream& out, tbv const& b) const { + if (num_tbits() == 0) return out << "[]"; + return display(out, b, num_tbits()-1, 0); +} + +expr_ref tbv_manager::to_formula(ast_manager& m, tbv const& src) { + expr_ref result(m); + expr_ref_vector conj(m); + for (unsigned i = 0; i < num_tbits(); ++i) { + switch (src[i]) { + case BIT_0: + conj.push_back(m.mk_not(m.mk_const(symbol(i), m.mk_bool_sort()))); + break; + case BIT_1: + conj.push_back(m.mk_const(symbol(i), m.mk_bool_sort())); + break; + default: + break; + } + } + result = mk_and(m, conj.size(), conj.c_ptr()); + return result; +} + +expr_ref tbv_manager::mk_var(ast_manager& m, unsigned i) { + return expr_ref(m.mk_const(symbol(i), m.mk_bool_sort()), m); +} diff --git a/src/muz/rel/tbv.h b/src/muz/rel/tbv.h new file mode 100644 index 000000000..e2e09d691 --- /dev/null +++ b/src/muz/rel/tbv.h @@ -0,0 +1,150 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + tbv.h + +Abstract: + + ternary bit-vector utilities. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-09-15 + +Revision History: + + +--*/ + +#ifndef TBV_H_ +#define TBV_H_ + +#include "fixed_bit_vector.h" +#include "rational.h" +#include "ast.h" + +class tbv; + +enum tbit { + BIT_z = 0x0, + BIT_0 = 0x1, + BIT_1 = 0x2, + BIT_x = 0x3 +}; + +inline tbit neg(tbit t) { + return (tbit)(t ^ 0x3); +} + +class tbv_manager { + friend class tbv; + fixed_bit_vector_manager m; + ptr_vector allocated_tbvs; +public: + tbv_manager(unsigned n): m(2*n) {} + ~tbv_manager(); + void reset(); + tbv* allocate(); + tbv* allocate1(); + tbv* allocate0(); + tbv* allocateX(); + tbv* allocate(tbv const& bv); + tbv* allocate(uint64 n); + tbv* allocate(rational const& r); + tbv* allocate(uint64 n, unsigned hi, unsigned lo); + tbv* allocate(tbv const& bv, unsigned const* permutation); + tbv* allocate(char const* bv); + + void deallocate(tbv* bv); + + unsigned get_size_estimate_bytes(const tbv&) const { return m.num_bytes(); } + + void copy(tbv& dst, tbv const& src) const; + unsigned num_tbits() const { return m.num_bits()/2; } + tbv& reset(tbv& bv) const { return fill0(bv); } + tbv& fill0(tbv& bv) const; + tbv& fill1(tbv& bv) const; + tbv& fillX(tbv& bv) const; + bool set_and(tbv& dst, tbv const& src) const; + tbv& set_or(tbv& dst, tbv const& src) const; + void complement(tbv const& src, ptr_vector& result); + bool equals(tbv const& a, tbv const& b) const; + unsigned hash(tbv const& src) const; + bool contains(tbv const& a, tbv const& b) const; + bool contains(tbv const& a, unsigned_vector const& colsa, + tbv const& b, unsigned_vector const& colsb) const; + bool intersect(tbv const& a, tbv const& b, tbv& result); + std::ostream& display(std::ostream& out, tbv const& b) const; + std::ostream& display(std::ostream& out, tbv const& b, unsigned hi, unsigned lo) const; + tbv* project(bit_vector const& to_delete, tbv const& src); + bool is_well_formed(tbv const& b) const; // - does not contain BIT_z; + void set(tbv& dst, uint64 n, unsigned hi, unsigned lo); + void set(tbv& dst, rational const& r, unsigned hi, unsigned lo); + void set(tbv& dst, tbv const& other, unsigned hi, unsigned lo); + void set(tbv& dst, unsigned index, tbit value); + + + static void debug_alloc(); + expr_ref to_formula(ast_manager& m, tbv const& src); + expr_ref mk_var(ast_manager& m, unsigned i); +}; + +class tbv: private fixed_bit_vector { + friend class fixed_bit_vector_manager; + friend class tbv_manager; + +public: + + struct eq { + tbv_manager& m; + eq(tbv_manager& m):m(m) {} + bool operator()(tbv const& d1, tbv const& d2) const { + return m.equals(d1, d2); + } + }; + + struct hash { + tbv_manager& m; + hash(tbv_manager& m):m(m) {} + unsigned operator()(tbv const& d) const { + return m.hash(d); + } + }; + + + tbit operator[](unsigned idx) const { return (tbit)get(idx); } + + +private: + + + unsigned get(unsigned index) const { + index *= 2; + return (fixed_bit_vector::get(index) << 1) | (unsigned)fixed_bit_vector::get(index+1); + } +}; + +class tbv_ref { + tbv_manager& mgr; + tbv* d; +public: + tbv_ref(tbv_manager& mgr):mgr(mgr),d(0) {} + tbv_ref(tbv_manager& mgr, tbv* d):mgr(mgr),d(d) {} + ~tbv_ref() { + if (d) mgr.deallocate(d); + } + tbv_ref& operator=(tbv* d2) { + if (d) mgr.deallocate(d); + d = d2; + return *this; + } + tbv& operator*() { return *d; } + tbv* operator->() { return d; } + tbv* get() { return d; } + tbv* detach() { tbv* result = d; d = 0; return result; } +}; + + +#endif /* TBV_H_ */ diff --git a/src/muz/rel/udoc_relation.cpp b/src/muz/rel/udoc_relation.cpp new file mode 100644 index 000000000..140d1e94d --- /dev/null +++ b/src/muz/rel/udoc_relation.cpp @@ -0,0 +1,1260 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + udoc_relation.cpp + +Abstract: + + Relation based on union of DOCs. + +Author: + + Nuno Lopes (a-nlopes) 2013-03-01 + Nikolaj Bjorner (nbjorner) 2014-09-15 + +Revision History: + + Revised version of dl_hassel_diff facilities. + +Notes: + +--*/ +#include "udoc_relation.h" +#include "dl_relation_manager.h" +#include "qe_util.h" +#include "ast_util.h" +#include "smt_kernel.h" + + +namespace datalog { + + udoc_relation::udoc_relation(udoc_plugin& p, relation_signature const& sig): + relation_base(p, sig), + dm(p.dm(p.num_signature_bits(sig))) { + unsigned column = 0; + for (unsigned i = 0; i < sig.size(); ++i) { + m_column_info.push_back(column); + column += p.num_sort_bits(sig[i]); + } + m_column_info.push_back(column); + } + udoc_relation::~udoc_relation() { + reset(); + } + void udoc_relation::reset() { + m_elems.reset(dm); + } + void udoc_relation::expand_column_vector(unsigned_vector& v, const udoc_relation* other) const { + unsigned_vector orig; + orig.swap(v); + for (unsigned i = 0; i < orig.size(); ++i) { + unsigned col, limit; + if (orig[i] < get_num_cols()) { + col = column_idx(orig[i]); + limit = col + column_num_bits(orig[i]); + } else { + unsigned idx = orig[i] - get_num_cols(); + col = get_num_bits() + other->column_idx(idx); + limit = col + other->column_num_bits(idx); + } + for (; col < limit; ++col) { + v.push_back(col); + } + } + } + + doc* udoc_relation::fact2doc(const relation_fact & f) const { + doc* d = dm.allocate0(); + for (unsigned i = 0; i < f.size(); ++i) { + unsigned bv_size; + rational val; + VERIFY(get_plugin().is_numeral(f[i], val, bv_size)); + SASSERT(bv_size == column_num_bits(i)); + unsigned lo = column_idx(i); + unsigned hi = column_idx(i + 1); + dm.tbvm().set(d->pos(),val, hi-1, lo); + } + return d; + } + void udoc_relation::add_fact(const relation_fact & f) { + doc* d = fact2doc(f); + m_elems.insert(dm, d); + } + void udoc_relation::add_new_fact(const relation_fact & f) { + m_elems.push_back(fact2doc(f)); + } + bool udoc_relation::empty() const { + return m_elems.is_empty_complete(get_plugin().m, dm); + } + bool udoc_relation::contains_fact(const relation_fact & f) const { + doc_ref d(dm, fact2doc(f)); + return m_elems.contains(dm, *d); + } + udoc_relation * udoc_relation::clone() const { + udoc_relation* result = udoc_plugin::get(get_plugin().mk_empty(get_signature())); + for (unsigned i = 0; i < m_elems.size(); ++i) { + result->m_elems.push_back(dm.allocate(m_elems[i])); + } + return result; + } + udoc_relation * udoc_relation::complement(func_decl* f) const { + udoc_relation* result = udoc_plugin::get(get_plugin().mk_empty(get_signature())); + m_elems.complement(dm, result->m_elems); + return result; + } + void udoc_relation::to_formula(expr_ref& fml) const { + ast_manager& m = fml.get_manager(); + expr_ref_vector disj(m); + for (unsigned i = 0; i < m_elems.size(); ++i) { + disj.push_back(to_formula(m_elems[i])); + } + fml = mk_or(m, disj.size(), disj.c_ptr()); + } + expr_ref udoc_relation::to_formula(doc const& d) const { + ast_manager& m = get_plugin().get_ast_manager(); + expr_ref result(m); + expr_ref_vector conjs(m); + conjs.push_back(to_formula(d.pos())); + for (unsigned i = 0; i < d.neg().size(); ++i) { + conjs.push_back(m.mk_not(to_formula(d.neg()[i]))); + } + result = mk_and(m, conjs.size(), conjs.c_ptr()); + return result; + } + expr_ref udoc_relation::to_formula(tbv const& t) const { + udoc_plugin& p = get_plugin(); + ast_manager& m = p.get_ast_manager(); + expr_ref result(m); + expr_ref_vector conjs(m); + for (unsigned i = 0; i < get_num_cols(); ++i) { + var_ref v(m); + v = m.mk_var(i, get_signature()[i]); + unsigned lo = column_idx(i); + unsigned hi = column_idx(i+1); + rational r(0); + unsigned lo1 = lo; + bool is_x = true; + for (unsigned j = lo; j < hi; ++j) { + switch(t[j]) { + case BIT_0: + if (is_x) is_x = false, lo1 = j, r.reset(); + break; + case BIT_1: + if (is_x) is_x = false, lo1 = j, r.reset(); + r += rational::power_of_two(j - lo1); + break; + case BIT_x: + if (!is_x) { + SASSERT(p.bv.is_bv_sort(get_signature()[i])); + conjs.push_back(m.mk_eq(p.bv.mk_extract(j-1-lo,lo1-lo,v), + p.bv.mk_numeral(r,j-lo1))); + } + is_x = true; + break; + default: + UNREACHABLE(); + } + } + if (!is_x) { + expr_ref num(m); + if (lo1 == lo) { + num = p.mk_numeral(r, get_signature()[i]); + conjs.push_back(m.mk_eq(v, num)); + } + else { + num = p.bv.mk_numeral(r, hi-lo1); + conjs.push_back(m.mk_eq(p.bv.mk_extract(hi-1-lo,lo1-lo,v), num)); + } + } + } + result = mk_and(m, conjs.size(), conjs.c_ptr()); + return result; + } + + udoc_plugin& udoc_relation::get_plugin() const { + return static_cast(relation_base::get_plugin()); + } + + void udoc_relation::display(std::ostream& out) const { + m_elems.display(dm, out); out << "\n"; + } + + unsigned udoc_relation::get_size_estimate_bytes() const { + return sizeof(*this) + m_elems.get_size_estimate_bytes(dm); + } + + // ------------- + + udoc_plugin::udoc_plugin(relation_manager& rm): + relation_plugin(udoc_plugin::get_name(), rm), + m(rm.get_context().get_manager()), + bv(m), + dl(m), + m_disable_fast_pass(false) { + } + udoc_plugin::~udoc_plugin() { + u_map::iterator it = m_dms.begin(), end = m_dms.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + } + udoc_relation& udoc_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + udoc_relation* udoc_plugin::get(relation_base* r) { + return r?dynamic_cast(r):0; + } + udoc_relation const & udoc_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + doc_manager& udoc_plugin::dm(relation_signature const& sig) { + return dm(num_signature_bits(sig)); + } + + doc_manager& udoc_plugin::dm(unsigned n) { + doc_manager* r; + if (!m_dms.find(n, r)) { + r = alloc(doc_manager, n); + m_dms.insert(n, r); + } + return *r; + } + bool udoc_relation::is_var_range(expr* e, unsigned& hi, unsigned& lo, unsigned& v) const { + udoc_plugin& p = get_plugin(); + if (is_var(e)) { + v = to_var(e)->get_idx(); + hi = p.num_sort_bits(e)-1; + lo = 0; + return true; + } + expr* e2; + if (p.bv.is_extract(e, lo, hi, e2) && is_var(e2)) { + v = to_var(e2)->get_idx(); + SASSERT(lo <= hi); + return true; + } + return false; + } + + expr* udoc_plugin::mk_numeral(rational const& r, sort* s) { + if (bv.is_bv_sort(s)) { + return bv.mk_numeral(r, s); + } + if (m.is_bool(s)) { + if (r.is_zero()) return m.mk_false(); + return m.mk_true(); + } + SASSERT(dl.is_finite_sort(s)); + return dl.mk_numeral(r.get_uint64(), s); + } + bool udoc_plugin::is_numeral(expr* e, rational& r, unsigned& num_bits) { + if (bv.is_numeral(e, r, num_bits)) return true; + if (m.is_true(e)) { + r = rational(1); + num_bits = 1; + return true; + } + if (m.is_false(e)) { + r = rational(0); + num_bits = 1; + return true; + } + uint64 n, sz; + ast_manager& m = get_ast_manager(); + if (dl.is_numeral(e, n) && dl.try_get_size(m.get_sort(e), sz)) { + num_bits = 0; + while (sz > 0) ++num_bits, sz = sz/2; + r = rational(n, rational::ui64()); + return true; + } + return false; + } + unsigned udoc_plugin::num_sort_bits(sort* s) const { + unsigned num_bits = 0; + if (bv.is_bv_sort(s)) + return bv.get_bv_size(s); + if (m.is_bool(s)) + return 1; + uint64 sz; + if (dl.try_get_size(s, sz)) { + while (sz > 0) ++num_bits, sz /= 2; + return num_bits; + } + UNREACHABLE(); + return 0; + } + unsigned udoc_plugin::num_signature_bits(relation_signature const& sig) { + unsigned result = 0; + for (unsigned i = 0; i < sig.size(); ++i) { + result += num_sort_bits(sig[i]); + } + return result; + } + + bool udoc_plugin::is_finite_sort(sort* s) const { + return bv.is_bv_sort(s) || dl.is_finite_sort(s); + } + + + bool udoc_plugin::can_handle_signature(const relation_signature & sig) { + for (unsigned i = 0; i < sig.size(); ++i) { + if (!is_finite_sort(sig[i])) + return false; + } + return true; + } + relation_base * udoc_plugin::mk_empty(const relation_signature & sig) { + return alloc(udoc_relation, *this, sig); + } + relation_base * udoc_plugin::mk_full(func_decl* p, const relation_signature & s) { + udoc_relation* r = get(mk_empty(s)); + r->get_udoc().push_back(dm(s).allocateX()); + return r; + } + class udoc_plugin::join_fn : public convenient_relation_join_fn { + doc_manager& dm; + doc_manager& dm1; + doc_manager& dm2; + public: + join_fn(udoc_plugin& p, udoc_relation const& t1, udoc_relation const& t2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2), + dm(p.dm(get_result_signature())), + dm1(t1.get_dm()), + dm2(t2.get_dm()) { + t1.expand_column_vector(m_cols1); + t2.expand_column_vector(m_cols2); + } + + virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + udoc_relation const& r1 = get(_r1); + udoc_relation const& r2 = get(_r2); + TRACE("doc", r1.display(tout << "r1:\n"); r2.display(tout << "r2:\n");); + udoc_plugin& p = r1.get_plugin(); + relation_signature const& sig = get_result_signature(); + udoc_relation * result = alloc(udoc_relation, p, sig); + udoc const& d1 = r1.get_udoc(); + udoc const& d2 = r2.get_udoc(); + udoc& r = result->get_udoc(); + r.join(d1, d2, dm, dm1, m_cols1, m_cols2); + TRACE("doc", result->display(tout << "result:\n");); + IF_VERBOSE(3, result->display(verbose_stream() << "join result:\n");); + SASSERT(r.well_formed(result->get_dm())); + return result; + } + }; + + + relation_join_fn * udoc_plugin::mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_fn, *this, get(t1), get(t2), col_cnt, cols1, cols2); + } + + class udoc_plugin::project_fn : public convenient_relation_project_fn { + bit_vector m_to_delete; + public: + project_fn(udoc_relation const & t, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(t.get_signature(), removed_col_cnt, removed_cols) { + t.expand_column_vector(m_removed_cols); + unsigned n = t.get_dm().num_tbits(); + m_to_delete.resize(n, false); + for (unsigned i = 0; i < m_removed_cols.size(); ++i) { + m_to_delete.set(m_removed_cols[i], true); + } + } + + virtual relation_base * operator()(const relation_base & tb) { + TRACE("doc", tb.display(tout << "src:\n");); + udoc_relation const& t = get(tb); + udoc_plugin& p = t.get_plugin(); + udoc_relation* r = udoc_plugin::get(p.mk_empty(get_result_signature())); + doc_manager& dm1 = t.get_dm(); + doc_manager& dm2 = r->get_dm(); + doc_ref d2(dm2); + udoc const& ud1 = t.get_udoc(); + udoc& ud2 = r->get_udoc(); + for (unsigned i = 0; i < ud1.size(); ++i) { + d2 = dm1.project(dm2, m_to_delete, ud1[i]); + ud2.push_back(d2.detach()); + } + TRACE("doc", tout << "final size: " << r->get_size_estimate_rows() << '\n';); + SASSERT(ud2.well_formed(dm2)); + return r; + } + }; + + + relation_transformer_fn * udoc_plugin::mk_project_fn( + const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) { + if (!check_kind(t)) + return 0; + return alloc(project_fn, get(t), col_cnt, removed_cols); + } + + class udoc_plugin::rename_fn : public convenient_relation_rename_fn { + unsigned_vector m_permutation; + public: + rename_fn(udoc_relation const& t, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(t.get_signature(), cycle_len, cycle) { + udoc_plugin& p = t.get_plugin(); + ast_manager& m = p.get_ast_manager(); + relation_signature const& sig1 = t.get_signature(); + relation_signature const& sig2 = get_result_signature(); + unsigned_vector permutation0, column_info; + for (unsigned i = 0; i < t.get_num_bits(); ++i) { + m_permutation.push_back(i); + } + for (unsigned i = 0; i < sig1.size(); ++i) { + permutation0.push_back(i); + } + for (unsigned i = 0; i < cycle_len; ++i) { + unsigned j = (i + 1)%cycle_len; + unsigned col1 = cycle[i]; + unsigned col2 = cycle[j]; + permutation0[col2] = col1; + } + unsigned column = 0; + for (unsigned i = 0; i < sig2.size(); ++i) { + column_info.push_back(column); + column += p.num_sort_bits(sig2[i]); + } + column_info.push_back(column); + SASSERT(column == t.get_num_bits()); + + TRACE("doc", + sig1.output(m, tout << "sig1: "); tout << "\n"; + sig2.output(m, tout << "sig2: "); tout << "\n"; + tout << "permute: "; + for (unsigned i = 0; i < permutation0.size(); ++i) { + tout << permutation0[i] << " "; + } + tout << "\n"; + tout << "cycle: "; + for (unsigned i = 0; i < cycle_len; ++i) { + tout << cycle[i] << " "; + } + tout << "\n"; + ); + + + // 0 -> 2 + // [3:2:1] -> [1:2:3] + // [3,4,5,1,2,0] + + for (unsigned i = 0; i < sig1.size(); ++i) { + unsigned len = t.column_num_bits(i); + unsigned lo1 = t.column_idx(i); + unsigned col2 = permutation0[i]; + unsigned lo2 = column_info[col2]; + SASSERT(lo2 + len <= t.get_num_bits()); + SASSERT(lo1 + len <= t.get_num_bits()); + for (unsigned k = 0; k < len; ++k) { + m_permutation[k + lo1] = k + lo2; + } + } + } + + virtual relation_base * operator()(const relation_base & _r) { + udoc_relation const& r = get(_r); + TRACE("doc", r.display(tout << "r:\n");); + udoc_plugin& p = r.get_plugin(); + relation_signature const& sig = get_result_signature(); + udoc_relation* result = alloc(udoc_relation, p, sig); + udoc const& src = r.get_udoc(); + udoc& dst = result->get_udoc(); + doc_manager& dm = r.get_dm(); + SASSERT(&result->get_dm() == &dm); + for (unsigned i = 0; i < src.size(); ++i) { + dst.push_back(dm.allocate(src[i], m_permutation.c_ptr())); + } + TRACE("doc", result->display(tout << "result:\n");); + SASSERT(dst.well_formed(dm)); + return result; + } + }; + relation_transformer_fn * udoc_plugin::mk_rename_fn( + const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if (check_kind(r)) { + return alloc(rename_fn, get(r), cycle_len, permutation_cycle); + } + else { + return 0; + } + } + class udoc_plugin::union_fn : public relation_union_fn { + public: + union_fn() {} + + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); + udoc_relation& r = get(_r); + udoc_relation const& src = get(_src); + udoc_relation* d = get(_delta); + doc_manager& dm = r.get_dm(); + udoc* d1 = 0; + if (d) d1 = &d->get_udoc(); + IF_VERBOSE(3, r.display(verbose_stream() << "orig: ");); + r.get_plugin().mk_union(dm, r.get_udoc(), src.get_udoc(), d1); + SASSERT(r.get_udoc().well_formed(dm)); + SASSERT(!d1 || d1->well_formed(dm)); + TRACE("doc", _r.display(tout << "dst':\n"); ); + IF_VERBOSE(3, r.display(verbose_stream() << "union: ");); + IF_VERBOSE(3, if (d) d->display(verbose_stream() << "delta: ");); + } + }; + void udoc_plugin::mk_union(doc_manager& dm, udoc& dst, udoc const& src, udoc* delta) { + bool deltaempty = delta ? delta->is_empty() : false; + + if (dst.is_empty()) { + for (unsigned i = 0; i < src.size(); ++i) { + dst.push_back(dm.allocate(src[i])); + if (delta) { + if (deltaempty) + delta->push_back(dm.allocate(src[i])); + else + delta->insert(dm, dm.allocate(src[i])); + } + } + } else { + for (unsigned i = 0; i < src.size(); ++i) { + if (dst.insert(dm, dm.allocate(src[i])) && delta) { + if (deltaempty) + delta->push_back(dm.allocate(src[i])); + else + delta->insert(dm, dm.allocate(src[i])); + } + } + } + } + relation_union_fn * udoc_plugin::mk_union_fn( + const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn); + } + relation_union_fn * udoc_plugin::mk_widen_fn( + const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + return mk_union_fn(tgt, src, delta); + } + + class udoc_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_cols; + unsigned m_size; + bit_vector m_empty_bv; + union_find_default_ctx union_ctx; + union_find<> m_equalities; + public: + filter_identical_fn(const relation_base & _r, unsigned col_cnt, const unsigned *identical_cols) + : m_cols(col_cnt), m_equalities(union_ctx) { + udoc_relation const& r = get(_r); + m_size = r.column_num_bits(identical_cols[0]); + m_empty_bv.resize(r.get_num_bits(), false); + for (unsigned i = 0; i < col_cnt; ++i) { + m_cols[i] = r.column_idx(identical_cols[i]); + } + for (unsigned i = 0, e = m_empty_bv.size(); i < e; ++i) { + m_equalities.mk_var(); + } + for (unsigned i = 1; i < col_cnt; ++i) { + for (unsigned j = 0; j < m_size; ++j) { + m_equalities.merge(m_cols[0]+j ,m_cols[i]+j); + } + } + } + + virtual void operator()(relation_base & _r) { + udoc_relation& r = get(_r); + udoc& d = r.get_udoc(); + doc_manager& dm = r.get_dm(); + d.merge(dm, m_cols[0], m_size, m_equalities, m_empty_bv); + SASSERT(d.well_formed(dm)); + TRACE("doc", tout << "final size: " << r.get_size_estimate_rows() << '\n';); + } + }; + relation_mutator_fn * udoc_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + return check_kind(t)?alloc(filter_identical_fn, t, col_cnt, identical_cols):0; + } + class udoc_plugin::filter_equal_fn : public relation_mutator_fn { + doc_manager& dm; + doc* m_filter; + public: + filter_equal_fn(udoc_plugin& p, const udoc_relation & t, const relation_element val, unsigned col): + dm(p.dm(t.get_signature())) { + rational r; + unsigned num_bits; + VERIFY(p.is_numeral(val, r, num_bits)); + m_filter = dm.allocateX(); + unsigned lo = t.column_idx(col); + unsigned hi = t.column_idx(col+1); + SASSERT(num_bits == hi - lo); + dm.tbvm().set(m_filter->pos(), r, hi-1, lo); + } + virtual ~filter_equal_fn() { + dm.deallocate(m_filter); + } + virtual void operator()(relation_base & tb) { + udoc_relation & t = get(tb); + t.get_udoc().intersect(dm, *m_filter); + SASSERT(t.get_udoc().well_formed(t.get_dm())); + } + }; + relation_mutator_fn * udoc_plugin::mk_filter_equal_fn( + const relation_base & t, const relation_element & value, unsigned col) { + if (!check_kind(t)) + return 0; + return alloc(filter_equal_fn, *this, get(t), value, col); + } + + bool udoc_relation::is_guard(unsigned n, expr* const* gs) const { + for (unsigned i = 0; i < n; ++i) { + if (!is_guard(gs[i])) return false; + } + return true; + } + bool udoc_relation::is_guard(expr* g) const { + udoc_plugin& p = get_plugin(); + ast_manager& m = p.get_ast_manager(); + bv_util& bv = p.bv; + expr* e1, *e2; + unsigned hi, lo, v; + if (m.is_and(g) || m.is_or(g) || m.is_not(g) || m.is_true(g) || m.is_false(g)) { + return is_guard(to_app(g)->get_num_args(), to_app(g)->get_args()); + } + if (m.is_eq(g, e1, e2) && bv.is_bv(e1)) { + if (is_var_range(e1, hi, lo, v) && is_ground(e2)) return true; + if (is_var_range(e2, hi, lo, v) && is_ground(e1)) return true; + } + if (is_var(g)) { + return true; + } + return false; + } + + void udoc_relation::extract_guard(expr* cond, expr_ref& guard, expr_ref& rest) const { + rest.reset(); + ast_manager& m = get_plugin().get_ast_manager(); + expr_ref_vector conds(m), guards(m), rests(m); + conds.push_back(cond); + flatten_and(conds); + for (unsigned i = 0; i < conds.size(); ++i) { + expr* g = conds[i].get(); + if (is_guard(g)) { + guards.push_back(g); + } + else { + rests.push_back(g); + } + } + guard = mk_and(m, guards.size(), guards.c_ptr()); + rest = mk_and(m, rests.size(), rests.c_ptr()); + } + void udoc_relation::extract_equalities(expr* g, expr_ref& rest, subset_ints& equalities, + unsigned_vector& roots) const { + rest.reset(); + ast_manager& m = get_plugin().get_ast_manager(); + expr_ref_vector conds(m); + conds.push_back(g); + flatten_and(conds); + expr* e1, *e2; + for (unsigned i = 0; i < conds.size(); ++i) { + expr* g = conds[i].get(); + if (m.is_eq(g, e1, e2)) { + extract_equalities(e1, e2, conds, equalities, roots); + conds[i] = conds.back(); + conds.pop_back(); + } + } + rest = mk_and(m, conds.size(), conds.c_ptr()); + } + + void udoc_relation::extract_equalities( + expr* e1, expr* e2, expr_ref_vector& conds, + subset_ints& equalities, unsigned_vector& roots) const { + udoc_plugin& p = get_plugin(); + ast_manager& m = p.get_ast_manager(); + bv_util& bv = p.bv; + th_rewriter rw(m); + unsigned hi, lo1, lo2, hi1, hi2, v1, v2; + if (bv.is_concat(e2)) { + std::swap(e1, e2); + } + if (bv.is_concat(e1)) { + expr_ref e3(m); + app* a1 = to_app(e1); + hi = p.num_sort_bits(e1)-1; + unsigned n = a1->get_num_args(); + for (unsigned i = 0; i < n; ++i) { + expr* e = a1->get_arg(i); + unsigned sz = p.num_sort_bits(e); + e3 = bv.mk_extract(hi, hi-sz+1, e2); + rw(e3); + extract_equalities(e, e3, conds, equalities, roots); + hi -= sz; + } + return; + } + if (is_var_range(e1, hi1, lo1, v1) && + is_var_range(e2, hi2, lo2, v2)) { + unsigned col1 = column_idx(v1); + lo1 += col1; + hi1 += col1; + unsigned col2 = column_idx(v2); + lo2 += col2; + hi2 += col2; + for (unsigned j = 0; j <= hi1-lo1; ++j) { + roots.push_back(lo1 + j); + equalities.merge(lo1 + j, lo2 + j); + } + return; + } + conds.push_back(m.mk_eq(e1, e2)); + } + + void udoc_relation::compile_guard(expr* g, udoc& result, bit_vector const& discard_cols) const { + result.push_back(dm.allocateX()); + + // datastructure to store equalities with columns that will be projected out + union_find_default_ctx union_ctx; + subset_ints equalities(union_ctx); + for (unsigned i = 0, e = discard_cols.size(); i < e; ++i) { + equalities.mk_var(); + } + apply_guard(g, result, equalities, discard_cols); + } + + bool udoc_relation::apply_ground_eq(doc_ref& d, unsigned v, unsigned hi, unsigned lo, expr* c) const { + udoc_plugin& p = get_plugin(); + unsigned num_bits; + rational r; + unsigned col = column_idx(v); + lo += col; + hi += col; + if (p.is_numeral(c, r, num_bits)) { + d = dm.allocateX(); + dm.tbvm().set(d->pos(), r, hi, lo); + return true; + } + // other cases? + return false; + } + + bool udoc_relation::apply_bv_eq( + expr* e1, expr* e2, bit_vector const& discard_cols, udoc& result) const { + udoc_plugin& p = get_plugin(); + ast_manager& m = p.get_ast_manager(); + bv_util& bv = p.bv; + th_rewriter rw(m); + doc_ref d(get_dm()); + unsigned hi, lo, lo1, lo2, hi1, hi2, v, v1, v2; + if (bv.is_concat(e2)) { + std::swap(e1, e2); + } + if (bv.is_concat(e1)) { + expr_ref e3(m); + app* a1 = to_app(e1); + hi = p.num_sort_bits(e1)-1; + unsigned n = a1->get_num_args(); + for (unsigned i = 0; i < n; ++i) { + expr* e = a1->get_arg(i); + unsigned sz = p.num_sort_bits(e); + e3 = bv.mk_extract(hi, hi-sz+1, e2); + rw(e3); + if (!apply_bv_eq(e, e3, discard_cols, result)) return false; + hi -= sz; + } + return true; + } + if (is_ground(e1)) { + std::swap(e1, e2); + } + if (is_var_range(e1, hi, lo, v) && is_ground(e2) && + apply_ground_eq(d, v, hi, lo, e2)) { + result.intersect(dm, *d); + return true; + } + if (is_var_range(e1, hi1, lo1, v1) && + is_var_range(e2, hi2, lo2, v2)) { + unsigned idx1 = lo1 + column_idx(v1); + unsigned idx2 = lo2 + column_idx(v2); + unsigned length = hi1-lo1+1; + result.merge(dm, idx1, idx2, length, discard_cols); + return true; + } + + return false; + } + + void udoc_relation::apply_guard( + expr* g, udoc& result, subset_ints const& equalities, bit_vector const& discard_cols) const { + ast_manager& m = get_plugin().get_ast_manager(); + bv_util& bv = get_plugin().bv; + expr *e0, *e1, *e2; + unsigned hi, lo, v; + doc_ref d(get_dm()); + if (result.is_empty()) { + } + else if (m.is_true(g)) { + } + else if (m.is_false(g)) { + result.reset(dm); + } + else if (m.is_and(g)) { + for (unsigned i = 0; !result.is_empty() && i < to_app(g)->get_num_args(); ++i) { + apply_guard(to_app(g)->get_arg(i), result, equalities, discard_cols); + } + } + else if (m.is_not(g, e0) && + m.is_eq(e0, e1, e2) && bv.is_bv(e1) && + is_var_range(e1, hi, lo, v) && is_ground(e2) && + apply_ground_eq(d, v, hi, lo, e2)) { + result.subtract(dm, *d); + } + else if (m.is_not(g, e0) && + m.is_eq(e0, e2, e1) && bv.is_bv(e1) && + is_var_range(e1, hi, lo, v) && is_ground(e2) && + apply_ground_eq(d, v, hi, lo, e2)) { + result.subtract(dm, *d); + } + else if (m.is_not(g, e1)) { + udoc sub; + sub.push_back(dm.allocateX()); + // TODO: right now we state that no columns are discarded to avoid + // silent column merging. This can be optimized if the set of merged + // columns is returned so that here we remove different columns. + bit_vector empty; + empty.resize(discard_cols.size(), false); + apply_guard(e1, sub, equalities, empty); + result.subtract(dm, sub); + result.simplify(dm); + TRACE("doc", + result.display(dm, tout << "result0:") << "\n"; + sub.display(dm, tout << "sub:") << "\n";); + sub.reset(dm); + TRACE("doc", result.display(dm, tout << "result:") << "\n";); + } + else if (m.is_or(g)) { + udoc sub; + sub.push_back(dm.allocateX()); + for (unsigned i = 0; !sub.is_empty() && i < to_app(g)->get_num_args(); ++i) { + expr_ref arg(m); + arg = mk_not(m, to_app(g)->get_arg(i)); + apply_guard(arg, sub, equalities, discard_cols); + } + TRACE("doc", result.display(dm, tout << "result0:") << "\n";); + result.subtract(dm, sub); + TRACE("doc", + sub.display(dm, tout << "sub:") << "\n"; + result.display(dm, tout << "result:") << "\n";); + sub.reset(dm); + } + else if (is_var(g)) { + SASSERT(m.is_bool(g)); + unsigned v = to_var(g)->get_idx(); + unsigned idx = column_idx(v); + doc_ref d(dm); + d = dm.allocateX(); + dm.set(*d, idx, BIT_1); + result.intersect(dm, *d); + } + else if ((m.is_eq(g, e1, e2) || m.is_iff(g, e1, e2)) && m.is_bool(e1)) { + udoc diff1, diff2; + diff1.push_back(dm.allocateX()); + diff2.push_back(dm.allocateX()); + expr_ref f1(m), f2(m); + f1 = mk_not(m, e1); + f2 = mk_not(m, e2); + apply_guard(e1, diff1, equalities, discard_cols); + apply_guard(f2, diff1, equalities, discard_cols); + result.subtract(dm, diff1); + diff1.reset(dm); + apply_guard(f1, diff2, equalities, discard_cols); + apply_guard(e2, diff2, equalities, discard_cols); + result.subtract(dm, diff2); + diff2.reset(dm); + } + else if (m.is_eq(g, e1, e2) && bv.is_bv(e1)) { + if (apply_bv_eq(e1, e2, discard_cols, result)) { + // done + } + else { + goto failure_case; + } + } + else { + failure_case: + std::ostringstream strm; + strm << "Guard expression is not handled" << mk_pp(g, m); + throw default_exception(strm.str()); + } + } + + class udoc_plugin::filter_interpreted_fn : public relation_mutator_fn { + union_find_default_ctx union_ctx; + doc_manager& dm; + expr_ref m_original_condition; + expr_ref m_reduced_condition; + udoc m_udoc; + bit_vector m_empty_bv; + subset_ints m_equalities; + + public: + filter_interpreted_fn(const udoc_relation & t, ast_manager& m, app *condition) : + dm(t.get_dm()), + m_original_condition(condition, m), + m_reduced_condition(m), + m_equalities(union_ctx) { + unsigned num_bits = t.get_num_bits(); + m_empty_bv.resize(num_bits, false); + expr_ref guard(m); + for (unsigned i = 0; i < num_bits; ++i) { + m_equalities.mk_var(); + } + t.extract_guard(condition, guard, m_reduced_condition); + m_udoc.push_back(dm.allocateX()); + t.apply_guard(guard, m_udoc, m_equalities, m_empty_bv); + + TRACE("doc", + tout << "original condition: " << mk_pp(condition, m) << "\n"; + tout << "remaining condition: " << m_reduced_condition << "\n"; + m_udoc.display(dm, tout) << "\n";); + } + + virtual ~filter_interpreted_fn() { + m_udoc.reset(dm); + } + + virtual void operator()(relation_base & tb) { + udoc_relation & t = get(tb); + udoc& u = t.get_udoc(); + SASSERT(u.well_formed(dm)); + u.intersect(dm, m_udoc); + SASSERT(u.well_formed(dm)); + t.apply_guard(m_reduced_condition, u, m_equalities, m_empty_bv); + SASSERT(u.well_formed(dm)); + u.simplify(dm); + SASSERT(u.well_formed(dm)); + TRACE("doc", tout << "final size: " << t.get_size_estimate_rows() << '\n';); + IF_VERBOSE(3, t.display(verbose_stream());); + } + }; + relation_mutator_fn * udoc_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + return check_kind(t)?alloc(filter_interpreted_fn, get(t), get_ast_manager(), condition):0; + } + + class udoc_plugin::join_project_fn : public convenient_relation_join_project_fn { +#if 0 + udoc_plugin::join_fn m_joiner; +#endif + bit_vector m_to_delete; + + public: + join_project_fn( + udoc_relation const& t1, udoc_relation const& t2, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, unsigned const* rm_cols) + : convenient_relation_join_project_fn( + t1.get_signature(), t2.get_signature(), + col_cnt, cols1, cols2, + removed_col_cnt, rm_cols) +#if 0 + , m_joiner(t1.get_plugin(), t1, t2, col_cnt, cols1, cols2) +#endif + { + unsigned num_bits1 = t1.get_num_bits(); + unsigned num_bits = num_bits1 + t2.get_num_bits(); + unsigned_vector removed_cols(removed_col_cnt, rm_cols); + t1.expand_column_vector(removed_cols, &t2); + t1.expand_column_vector(m_cols1); + t2.expand_column_vector(m_cols2); + m_to_delete.resize(num_bits, false); + for (unsigned i = 0; i < removed_cols.size(); ++i) { + m_to_delete.set(removed_cols[i], true); + } + } + + + // TBD: replace this by "join" given below. + virtual relation_base* operator()(relation_base const& t1, relation_base const& t2) { +#if 1 + return join(get(t1), get(t2)); +#else + udoc_relation *joined = get(m_joiner(t1, t2)); + relation_base* result = 0; + if (joined->fast_empty()) { + result = t1.get_plugin().mk_empty(get_result_signature()); + } + else { + project_fn projector(*joined, m_removed_cols.size(), m_removed_cols.c_ptr()); + result = projector(*joined); + } + joined->deallocate(); + return result; +#endif + } + private: + + udoc_relation* join(udoc_relation const& t1, udoc_relation const& t2) { + relation_signature prod_signature; + prod_signature.append(t1.get_signature()); + prod_signature.append(t2.get_signature()); + udoc const& d1 = t1.get_udoc(); + udoc const& d2 = t2.get_udoc(); + doc_manager& dm1 = t1.get_dm(); + udoc_plugin& p = t1.get_plugin(); + doc_manager& dm_prod = p.dm(prod_signature); + udoc_relation* result = get(p.mk_empty(get_result_signature())); + udoc& res = result->get_udoc(); + doc_manager& dm_res = result->get_dm(); + + for (unsigned i = 0; i < d1.size(); ++i) { + for (unsigned j = 0; j < d2.size(); ++j) { + doc_ref d(dm_prod, dm_prod.join(d1[i], d2[j], dm1, m_cols1, m_cols2)); + if (d) { + res.insert(dm_res, dm_prod.project(dm_res, m_to_delete, *d)); + IF_VERBOSE(2, + if (res.size() > 0 && 0 == res.size() % 10000) { + verbose_stream() << "result size: " << res.size() + << " i:" << i << " j:" << j << " " + << 100*i/d1.size() << "% complete\n"; + }); + } + } + } + TRACE("doc", result->display(tout);); + return result; + } + }; + + + class udoc_plugin::join_project_and_fn : public relation_join_fn { + public: + join_project_and_fn() {} + + virtual relation_base* operator()(relation_base const& t1, relation_base const& t2) { + udoc_relation *result = get(t1.clone()); + result->get_udoc().intersect(result->get_dm(), get(t2).get_udoc()); + return result; + } + }; + + relation_join_fn * udoc_plugin::mk_join_project_fn( + relation_base const& t1, relation_base const& t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) { + if (!check_kind(t1) || !check_kind(t2)) + return 0; + // special case where we have h(X) :- f(X), g(X). + if (joined_col_cnt == removed_col_cnt && + t1.get_signature().size() == joined_col_cnt && + t2.get_signature().size() == joined_col_cnt) { + for (unsigned i = 0; i < removed_col_cnt; ++i) { + if (removed_cols[i] != i || cols1[i] != cols2[i]) + goto general_fn; + } + return alloc(join_project_and_fn); + } + + general_fn: + return alloc(join_project_fn, get(t1), get(t2), + joined_col_cnt, cols1, cols2, + removed_col_cnt, removed_cols); + } + + + // + // Notes: + // 1. this code could use some cleanup and simplification. + // 2. It is also not very efficient in the copy routines. + // They fall back to copying each bit instead of a chunk. + // 3. Argument about correctness is needed as comments. + // 4. Unit/stress test cases are needed. + // + class udoc_plugin::negation_filter_fn : public relation_intersection_filter_fn { + struct mk_remove_cols { + mk_remove_cols(relation_base const& t1, relation_base const& t2, unsigned_vector& remove_cols) { + unsigned sz1 = t1.get_signature().size(); + unsigned sz2 = t2.get_signature().size(); + for (unsigned i = 0; i < sz2; ++i) { + remove_cols.push_back(sz1 + i); + } + } + }; + unsigned_vector m_t_cols; + unsigned_vector m_neg_cols; + unsigned_vector m_remove_cols; + mk_remove_cols m_mk_remove_cols; + join_project_fn m_join_project; + bool m_is_subtract; + //bool m_is_aliased; + public: + negation_filter_fn(const udoc_relation & t, const udoc_relation & neg, unsigned joined_col_cnt, + const unsigned *t_cols, const unsigned *neg_cols) + : m_t_cols(joined_col_cnt, t_cols), + m_neg_cols(joined_col_cnt, neg_cols), + m_mk_remove_cols(t, neg, m_remove_cols), + m_join_project(t, neg, joined_col_cnt, t_cols, neg_cols, + m_remove_cols.size(), m_remove_cols.c_ptr()), + m_is_subtract(false)//, + /*m_is_aliased(true) */{ + SASSERT(joined_col_cnt > 0 || neg.get_signature().size() == 0); + m_is_subtract = (joined_col_cnt == t.get_signature().size()); + m_is_subtract &= (joined_col_cnt == neg.get_signature().size()); + svector found(joined_col_cnt, false); + for (unsigned i = 0; m_is_subtract && i < joined_col_cnt; ++i) { + m_is_subtract = !found[t_cols[i]] && (t_cols[i] == neg_cols[i]); + found[t_cols[i]] = true; + } + t.expand_column_vector(m_t_cols); + neg.expand_column_vector(m_neg_cols); + } + + virtual void operator()(relation_base& tb, const relation_base& negb) { + udoc_relation& t = get(tb); + udoc_relation const& n = get(negb); + IF_VERBOSE(3, t.display(verbose_stream() << "dst:");); + IF_VERBOSE(3, n.display(verbose_stream() << "neg:");); + if (t.fast_empty() || n.fast_empty()) + return; + + /* TODO: double check + if (!m_is_aliased && !p.m_disable_fast_pass) { + // fast_pass(t, n); + } + */ + if (n.get_signature().empty()) + t.get_udoc().reset(t.get_dm()); + else if (m_is_subtract) + t.get_udoc().subtract(t.get_dm(), n.get_udoc()); + else + slow_pass(t, n); + } + + private: + /* + void fast_pass(udoc_relation& t, const udoc_relation& n) { + SASSERT(!m_is_aliased); + udoc & dst = t.get_udoc(); + udoc const & neg = n.get_udoc(); + doc_manager& dmt = t.get_dm(); + doc_manager& dmn = n.get_dm(); + udoc result; + for (unsigned i = 0; i < dst.size(); ++i) { + bool subsumed = false; + for (unsigned j = 0; j < neg.size(); ++j) { + if (dmn.contains(neg[j], m_neg_cols, dst[i], m_t_cols)) { + dmt.deallocate(&dst[i]); + subsumed = true; + break; + } + } + if (!subsumed) + result.push_back(&dst[i]); + } + std::swap(dst, result); + } + */ + + void slow_pass(udoc_relation& t, udoc_relation const& n) { + doc_manager& dmt = t.get_dm(); + udoc_relation* jp = get(m_join_project(t, n)); + if (!jp->fast_empty()) { + t.get_udoc().subtract(dmt, jp->get_udoc()); + } + TRACE("doc", t.display(tout); tout << "\n"; jp->display(tout); tout << "\n";); + jp->deallocate(); + } + }; + + relation_intersection_filter_fn * udoc_plugin::mk_filter_by_negation_fn( + const relation_base& t, + const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, + const unsigned *negated_cols) { + if (!check_kind(t) || !check_kind(neg)) + return 0; + return alloc(negation_filter_fn, get(t), get(neg), joined_col_cnt, t_cols, negated_cols); + } + + + + class udoc_plugin::filter_proj_fn : public convenient_relation_project_fn { + union_find_default_ctx union_ctx; + doc_manager& dm; + expr_ref m_original_condition; + expr_ref m_reduced_condition; + udoc m_udoc; + udoc m_udoc2; + bit_vector m_to_delete; // map: col idx -> bool (whether the column is to be removed) + subset_ints m_equalities; + unsigned_vector m_roots; + + public: + filter_proj_fn(const udoc_relation & t, ast_manager& m, app *condition, + unsigned col_cnt, const unsigned * removed_cols) : + convenient_relation_project_fn(t.get_signature(), col_cnt, removed_cols), + dm(t.get_dm()), + m_original_condition(condition, m), + m_reduced_condition(m), + m_equalities(union_ctx) { + unsigned num_bits = t.get_num_bits(); + t.expand_column_vector(m_removed_cols); + m_to_delete.resize(num_bits, false); + for (unsigned i = 0; i < num_bits; ++i) { + m_equalities.mk_var(); + } + for (unsigned i = 0; i < m_removed_cols.size(); ++i) { + m_to_delete.set(m_removed_cols[i], true); + } + expr_ref guard(m), non_eq_cond(condition, m); + t.extract_equalities(condition, non_eq_cond, m_equalities, m_roots); + t.extract_guard(non_eq_cond, guard, m_reduced_condition); + t.compile_guard(guard, m_udoc, m_to_delete); + } + + virtual ~filter_proj_fn() { + m_udoc.reset(dm); + } + virtual relation_base* operator()(const relation_base & tb) { + udoc_relation const & t = get(tb); + udoc const& u1 = t.get_udoc(); + doc_manager& dm = t.get_dm(); + m_udoc2.copy(dm, u1); + m_udoc2.intersect(dm, m_udoc); + t.apply_guard(m_reduced_condition, m_udoc2, m_equalities, m_to_delete); + m_udoc2.merge(dm, m_roots, m_equalities, m_to_delete); + SASSERT(m_udoc2.well_formed(dm)); + udoc_relation* r = get(t.get_plugin().mk_empty(get_result_signature())); + doc_manager& dm2 = r->get_dm(); + for (unsigned i = 0; i < m_udoc2.size(); ++i) { + doc* d = dm.project(dm2, m_to_delete, m_udoc2[i]); + r->get_udoc().insert(dm2, d); + SASSERT(r->get_udoc().well_formed(dm2)); + } + m_udoc2.reset(dm); + IF_VERBOSE(3, r->display(verbose_stream() << "filter project result:\n");); + return r; + } + }; + relation_transformer_fn * udoc_plugin::mk_filter_interpreted_and_project_fn( + const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols) { + return check_kind(t)?alloc(filter_proj_fn, get(t), get_ast_manager(), condition, removed_col_cnt, removed_cols):0; + } + + + + + +} diff --git a/src/muz/rel/udoc_relation.h b/src/muz/rel/udoc_relation.h new file mode 100644 index 000000000..026abfab0 --- /dev/null +++ b/src/muz/rel/udoc_relation.h @@ -0,0 +1,153 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + udoc_relation.h + +Abstract: + + Relation based on union of DOCs. + +Author: + + Nuno Lopes (a-nlopes) 2013-03-01 + Nikolaj Bjorner (nbjorner) 2014-09-15 + +Revision History: + + +--*/ + +#ifndef UDOC_RELATION_H_ +#define UDOC_RELATION_H_ + +#include "doc.h" +#include "dl_base.h" + +namespace datalog { + class udoc_plugin; + class udoc_relation; + + class udoc_relation : public relation_base { + friend class udoc_plugin; + doc_manager& dm; + mutable udoc m_elems; + unsigned_vector m_column_info; + doc* fact2doc(relation_fact const& f) const; + expr_ref to_formula(tbv const& t) const; + expr_ref to_formula(doc const& d) const; + public: + udoc_relation(udoc_plugin& p, relation_signature const& s); + virtual ~udoc_relation(); + virtual void reset(); + virtual void add_fact(const relation_fact & f); + virtual void add_new_fact(const relation_fact & f); + virtual bool contains_fact(const relation_fact & f) const; + virtual udoc_relation * clone() const; + virtual udoc_relation * complement(func_decl*) const; + virtual void to_formula(expr_ref& fml) const; + udoc_plugin& get_plugin() const; + virtual bool fast_empty() const { return m_elems.is_empty(); } + virtual bool empty() const; + virtual void display(std::ostream& out) const; + virtual bool is_precise() const { return true; } + virtual unsigned get_size_estimate_rows() const { return m_elems.size(); } + virtual unsigned get_size_estimate_bytes() const; + + doc_manager& get_dm() const { return dm; } + udoc const& get_udoc() const { return m_elems; } + udoc& get_udoc() { return m_elems; } + unsigned get_num_records() const { return m_elems.size(); } + unsigned get_num_bits() const { return m_column_info.back(); } + unsigned get_num_cols() const { return m_column_info.size()-1; } + unsigned column_idx(unsigned col) const { return m_column_info[col]; } + unsigned column_num_bits(unsigned col) const { return m_column_info[col+1] - m_column_info[col]; } + void expand_column_vector(unsigned_vector& v, const udoc_relation* other = 0) const; + void extract_guard(expr* condition, expr_ref& guard, expr_ref& rest) const; + bool is_guard(expr* g) const; + bool is_guard(unsigned n, expr* const *g) const; + void compile_guard(expr* g, udoc& result, bit_vector const& discard_cols) const; + void extract_equalities(expr* g, expr_ref& rest, subset_ints& equalities, unsigned_vector& roots) const; + void extract_equalities( + expr* e1, expr* e2, expr_ref_vector& conds, + subset_ints& equalities, unsigned_vector& roots) const; + void apply_guard(expr* g, udoc& result, subset_ints const& equalities, bit_vector const& discard_cols) const; + bool apply_ground_eq(doc_ref& d, unsigned v, unsigned hi, unsigned lo, expr* c) const; + bool apply_bv_eq(expr* e1, expr* e2, bit_vector const& discard_cols, udoc& result) const; + bool is_var_range(expr* e, unsigned& hi, unsigned& lo, unsigned& v) const; + }; + + class udoc_plugin : public relation_plugin { + friend class udoc_relation; + class join_fn; + class join_project_fn; + class join_project_and_fn; + class project_fn; + class union_fn; + class rename_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + class filter_by_negation_fn; + class filter_by_union_fn; + class filter_proj_fn; + class negation_filter_fn; + ast_manager& m; + bv_util bv; + dl_decl_util dl; + u_map m_dms; + bool m_disable_fast_pass; + + doc_manager& dm(unsigned sz); + doc_manager& dm(relation_signature const& sig); + static udoc_relation& get(relation_base& r); + static udoc_relation* get(relation_base* r); + static udoc_relation const & get(relation_base const& r); + void mk_union(doc_manager& dm, udoc& dst, udoc const& src, udoc* delta); + bool is_numeral(expr* e, rational& r, unsigned& num_bits); + unsigned num_sort_bits(expr* e) const { return num_sort_bits(get_ast_manager().get_sort(e)); } + unsigned num_sort_bits(sort* s) const; + bool is_finite_sort(sort* s) const; + unsigned num_signature_bits(relation_signature const& sig); + expr* mk_numeral(rational const& r, sort* s); + public: + udoc_plugin(relation_manager& rm); + ~udoc_plugin(); + virtual bool can_handle_signature(const relation_signature & s); + static symbol get_name() { return symbol("doc"); } + virtual relation_base * mk_empty(const relation_signature & s); + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + virtual relation_intersection_filter_fn * mk_filter_by_negation_fn( + const relation_base& t, + const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, + const unsigned *negated_cols); + virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn( + const relation_base & t, app * condition, + unsigned removed_col_cnt, const unsigned * removed_cols); + virtual relation_join_fn * mk_join_project_fn( + relation_base const& t1, relation_base const& t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols); + + void disable_fast_pass() { m_disable_fast_pass = true; } + }; +}; + +#endif /* UDOC_RELATION_H_ */ + diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index f13590e35..472b28292 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -31,6 +31,7 @@ Revision History: #include "matcher.h" #include "scoped_proof.h" #include "fixedpoint_params.hpp" +#include "ast_util.h" namespace tb { @@ -210,17 +211,19 @@ namespace tb { fmls.push_back(m_predicates[i]); } fmls.push_back(m_constraint); - qe::flatten_and(fmls); + flatten_and(fmls); bool_rewriter(m).mk_and(fmls.size(), fmls.c_ptr(), fml); return fml; } void get_free_vars(ptr_vector& vars) const { - ::get_free_vars(m_head, vars); + expr_free_vars fv; + fv(m_head); for (unsigned i = 0; i < m_predicates.size(); ++i) { - ::get_free_vars(m_predicates[i], vars); + fv.accumulate(m_predicates[i]); } - ::get_free_vars(m_constraint, vars); + fv.accumulate(m_constraint); + vars.append(fv.size(), fv.c_ptr()); } expr_ref to_formula() const { @@ -339,7 +342,7 @@ namespace tb { expr_ref tmp(m); substitution subst(m); subst.reserve(1, get_num_vars()); - qe::flatten_and(m_constraint, fmls); + flatten_and(m_constraint, fmls); unsigned num_fmls = fmls.size(); for (unsigned i = 0; i < num_fmls; ++i) { if (get_subst(rw, subst, i, fmls)) { @@ -666,7 +669,7 @@ namespace tb { m_qe(m_empty_set, false, fmls); - qe::flatten_and(fmls); + flatten_and(fmls); for (unsigned i = 0; i < fmls.size(); ++i) { expr_ref n = normalize(fmls[i].get()); if (m_sat_lits.contains(n)) { @@ -763,7 +766,7 @@ namespace tb { m_weight_multiply(1.0), m_update_frequency(20), m_next_update(20) { - set_strategy(ctx.get_params().tab_selection()); + set_strategy(ctx.tab_selection()); } void init(rules const& rs) { @@ -1107,16 +1110,16 @@ namespace tb { m_S1.apply(2, delta, expr_offset(tgt.get_constraint(), 0), tmp); m_S1.apply(2, delta, expr_offset(src.get_constraint(), 1), tmp2); constraint = m.mk_and(tmp, tmp2); - ptr_vector vars; // perform trival quantifier-elimination: uint_set index_set; - get_free_vars(head, vars); + expr_free_vars fv; + fv(head); for (unsigned i = 0; i < predicates.size(); ++i) { - get_free_vars(predicates[i].get(), vars); + fv.accumulate(predicates[i].get()); } - for (unsigned i = 0; i < vars.size(); ++i) { - if (vars[i]) { + for (unsigned i = 0; i < fv.size(); ++i) { + if (fv[i]) { index_set.insert(i); } } @@ -1127,7 +1130,7 @@ namespace tb { // initialize rule. result->init(head, predicates, constraint); - vars.reset(); + ptr_vector vars; result->get_free_vars(vars); bool change = false; var_ref w(m); diff --git a/src/muz/tab/tab_context.h b/src/muz/tab/tab_context.h index 4689598c0..f9b22a418 100644 --- a/src/muz/tab/tab_context.h +++ b/src/muz/tab/tab_context.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _TAB_CONTEXT_H_ -#define _TAB_CONTEXT_H_ +#ifndef TAB_CONTEXT_H_ +#define TAB_CONTEXT_H_ #include "ast.h" #include "lbool.h" diff --git a/src/muz/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 641d40779..82d351113 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -18,9 +18,10 @@ Revision History: --*/ #include "dl_mk_array_blast.h" -#include "qe_util.h" +#include "ast_util.h" #include "scoped_proof.h" + namespace datalog { @@ -115,7 +116,7 @@ namespace datalog { bool mk_array_blast::ackermanize(rule const& r, expr_ref& body, expr_ref& head) { expr_ref_vector conjs(m), trail(m); - qe::flatten_and(body, conjs); + flatten_and(body, conjs); m_defs.reset(); m_next_var = 0; ptr_vector todo; @@ -246,7 +247,7 @@ namespace datalog { for (unsigned i = utsz; i < tsz; ++i) { conjs.push_back(r.get_tail(i)); } - qe::flatten_and(conjs); + flatten_and(conjs); for (unsigned i = 0; i < conjs.size(); ++i) { expr* x, *y, *e = conjs[i].get(); @@ -266,11 +267,13 @@ namespace datalog { } else { m_rewriter(e, tmp); - change = change || (tmp != e); new_conjs.push_back(tmp); } } - + if (!inserted) { + rules.add_rule(&r); + return false; + } expr_ref fml1(m), fml2(m), body(m), head(m); body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); head = r.get_head(); @@ -278,8 +281,8 @@ namespace datalog { m_rewriter(body); sub(head); m_rewriter(head); - change = ackermanize(r, body, head) || change; - if (!inserted && !change) { + change = ackermanize(r, body, head); + if (!change) { rules.add_rule(&r); return false; } @@ -287,6 +290,7 @@ namespace datalog { fml2 = m.mk_implies(body, head); proof_ref p(m); rule_set new_rules(m_ctx); + TRACE("dl", tout << fml2 << "\n";); rm.mk_rule(fml2, p, new_rules, r.name()); @@ -294,7 +298,7 @@ namespace datalog { if (m_simplifier.transform_rule(new_rules.last(), new_rule)) { if (r.get_proof()) { scoped_proof _sc(m); - r.to_formula(fml1); + rm.to_formula(r, fml1); p = m.mk_rewrite(fml1, fml2); p = m.mk_modus_ponens(r.get_proof(), p); new_rule->set_proof(m, p); diff --git a/src/muz/transforms/dl_mk_array_blast.h b/src/muz/transforms/dl_mk_array_blast.h index c96573848..0bec9ae14 100644 --- a/src/muz/transforms/dl_mk_array_blast.h +++ b/src/muz/transforms/dl_mk_array_blast.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_ARRAY_BLAST_H_ -#define _DL_MK_ARRAY_BLAST_H_ +#ifndef DL_MK_ARRAY_BLAST_H_ +#define DL_MK_ARRAY_BLAST_H_ #include"dl_context.h" #include"dl_rule_set.h" @@ -74,5 +74,5 @@ namespace datalog { }; -#endif /* _DL_MK_ARRAY_BLAST_H_ */ +#endif /* DL_MK_ARRAY_BLAST_H_ */ diff --git a/src/muz/transforms/dl_mk_backwards.h b/src/muz/transforms/dl_mk_backwards.h index 4e546c848..135f89a18 100644 --- a/src/muz/transforms/dl_mk_backwards.h +++ b/src/muz/transforms/dl_mk_backwards.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_BACKWARDS_H_ -#define _DL_MK_BACKWARDS_H_ +#ifndef DL_MK_BACKWARDS_H_ +#define DL_MK_BACKWARDS_H_ #include"dl_rule_transformer.h" @@ -34,5 +34,5 @@ namespace datalog { }; -#endif /* _DL_MK_BACKWARDS_H_ */ +#endif /* DL_MK_BACKWARDS_H_ */ diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 9b451811c..6d1225641 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -225,7 +225,6 @@ namespace datalog { mk_interp_tail_simplifier m_simplifier; bit_blaster_rewriter m_blaster; expand_mkbv m_rewriter; - bool blast(rule *r, expr_ref& fml) { proof_ref pr(m); @@ -235,7 +234,7 @@ namespace datalog { if (!m_simplifier.transform_rule(r, r2)) { r2 = r; } - r2->to_formula(fml1); + m_context.get_rule_manager().to_formula(*r2.get(), fml1); m_blaster(fml1, fml2, pr); m_rewriter(fml2, fml3); TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml2, m) << " -> " << mk_pp(fml3, m) << "\n";); @@ -263,7 +262,7 @@ namespace datalog { rule_set * operator()(rule_set const & source) { // TODO pc - if (!m_context.bit_blast()) { + if (!m_context.xform_bit_blast()) { return 0; } rule_manager& rm = m_context.get_rule_manager(); @@ -274,7 +273,7 @@ namespace datalog { m_rewriter.m_cfg.set_dst(result); for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { rule * r = source.get_rule(i); - r->to_formula(fml); + rm.to_formula(*r, fml); if (blast(r, fml)) { proof_ref pr(m); if (r->get_proof()) { diff --git a/src/muz/transforms/dl_mk_bit_blast.h b/src/muz/transforms/dl_mk_bit_blast.h index da91c5804..8111e8849 100644 --- a/src/muz/transforms/dl_mk_bit_blast.h +++ b/src/muz/transforms/dl_mk_bit_blast.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_BIT_BLAST_H_ -#define _DL_MK_BIT_BLAST_H_ +#ifndef DL_MK_BIT_BLAST_H_ +#define DL_MK_BIT_BLAST_H_ #include"dl_rule_transformer.h" @@ -34,5 +34,5 @@ namespace datalog { }; -#endif /* _DL_MK_BIT_BLAST_H_ */ +#endif /* DL_MK_BIT_BLAST_H_ */ diff --git a/src/muz/transforms/dl_mk_coalesce.cpp b/src/muz/transforms/dl_mk_coalesce.cpp index ac7a58d8d..7476a5655 100644 --- a/src/muz/transforms/dl_mk_coalesce.cpp +++ b/src/muz/transforms/dl_mk_coalesce.cpp @@ -134,9 +134,9 @@ namespace datalog { is_neg.push_back(false); res = rm.mk(head, tail.size(), tail.c_ptr(), is_neg.c_ptr(), tgt->name()); if (m_ctx.generate_proof_trace()) { - src.to_formula(fml1); - tgt->to_formula(fml2); - res->to_formula(fml); + rm.to_formula(src, fml1); + rm.to_formula(*tgt.get(),fml2); + rm.to_formula(*res.get(),fml); #if 0 sort* ps = m.mk_proof_sort(); sort* domain[3] = { ps, ps, m.mk_bool_sort() }; diff --git a/src/muz/transforms/dl_mk_coalesce.h b/src/muz/transforms/dl_mk_coalesce.h index 4a4065174..9b4d009ae 100644 --- a/src/muz/transforms/dl_mk_coalesce.h +++ b/src/muz/transforms/dl_mk_coalesce.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_COALESCE_H_ -#define _DL_MK_COALESCE_H_ +#ifndef DL_MK_COALESCE_H_ +#define DL_MK_COALESCE_H_ #include"dl_context.h" #include"dl_rule_set.h" @@ -57,5 +57,5 @@ namespace datalog { }; -#endif /* _DL_MK_COALESCE_H_ */ +#endif /* DL_MK_COALESCE_H_ */ diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index cdb4a5b9e..67b88724c 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2006 Microsoft Corporation +Copyright (c) 2006-2015 Microsoft Corporation Module Name: @@ -7,174 +7,98 @@ Module Name: Abstract: - Rule transformer which removes relations which are out of the cone of + Rule transformer which removes relations which are out of the cone of influence of output relations Author: - - Krystof Hoder (t-khoder) 2011-10-01. - -Revision History: - - Andrey Rybalchenko (rybal) 2013-8-8 - Added bottom_up pruning. + Krystof Hoder (t-khoder) + Andrey Rybalchenko (rybal) + Henning Guenther (t-hennig) --*/ - -#include -#include"ast_pp.h" -#include"dl_mk_coi_filter.h" -#include"extension_model_converter.h" +#include "dl_mk_coi_filter.h" +#include "dataflow.h" +#include "reachability.h" +#include "ast_pp.h" +#include "extension_model_converter.h" namespace datalog { - - // ----------------------------------- - // - // mk_coi_filter - // - // ----------------------------------- - rule_set * mk_coi_filter::operator()(rule_set const & source) { - if (source.empty()) { - return 0; - } scoped_ptr result1 = top_down(source); - scoped_ptr result2 = bottom_up(result1?*result1:source); - if (!result2) { - result2 = result1.detach(); - } - return result2.detach(); + scoped_ptr result2 = bottom_up(result1 ? *result1 : source); + return result2 ? result2.detach() : result1.detach(); } rule_set * mk_coi_filter::bottom_up(rule_set const & source) { - func_decl_set all, reached; - ptr_vector todo; - rule_set::decl2rules body2rules; - // initialization for reachability - for (rule_set::iterator it = source.begin(); it != source.end(); ++it) { - rule * r = *it; - all.insert(r->get_decl()); - if (r->get_uninterpreted_tail_size() == 0) { - if (!reached.contains(r->get_decl())) { - reached.insert(r->get_decl()); - todo.insert(r->get_decl()); - } - } - else { - for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { - func_decl * d = r->get_tail(i)->get_decl(); - all.insert(d); - rule_set::decl2rules::obj_map_entry * e = body2rules.insert_if_not_there2(d, 0); - if (!e->get_data().m_value) { - e->get_data().m_value = alloc(ptr_vector); - } - e->get_data().m_value->push_back(r); - } - } - } - rel_context_base* rc = m_context.get_rel_context(); - if (rc) { - func_decl_set::iterator fit = all.begin(), fend = all.end(); - for (; fit != fend; ++fit) { - if (!rc->is_empty_relation(*fit) && - !reached.contains(*fit)) { - reached.insert(*fit); - todo.insert(*fit); - } - } - } - // reachability computation - while (!todo.empty()) { - func_decl * d = todo.back(); - todo.pop_back(); - ptr_vector * rules; - if (!body2rules.find(d, rules)) continue; - for (ptr_vector::iterator it = rules->begin(); it != rules->end(); ++it) { - rule * r = *it; - if (reached.contains(r->get_decl())) continue; - bool contained = true; - for (unsigned i = 0; contained && i < r->get_uninterpreted_tail_size(); ++i) { - contained = reached.contains(r->get_tail(i)->get_decl()); - } - if (!contained) continue; - reached.insert(r->get_decl()); - todo.insert(r->get_decl()); - } - } - - // eliminate each rule when some body predicate is not reached + dataflow_engine engine(source.get_manager(), source); + engine.run_bottom_up(); scoped_ptr res = alloc(rule_set, m_context); res->inherit_predicates(source); for (rule_set::iterator it = source.begin(); it != source.end(); ++it) { rule * r = *it; + bool new_tail = false; bool contained = true; - for (unsigned i = 0; contained && i < r->get_uninterpreted_tail_size(); ++i) { - contained = reached.contains(r->get_tail(i)->get_decl()); + for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { + if (r->is_neg_tail(i)) { + if (!engine.get_fact(r->get_decl(i)).is_reachable()) { + if (!new_tail) { + for (unsigned j = 0; j < i; ++j) { + m_new_tail.push_back(r->get_tail(j)); + m_new_tail_neg.push_back(r->is_neg_tail(j)); + } + new_tail = true; + } + } else if (new_tail) { + m_new_tail.push_back(r->get_tail(i)); + m_new_tail_neg.push_back(true); + } + } else { + SASSERT(!new_tail); + if (!engine.get_fact(r->get_decl(i)).is_reachable()) { + contained = false; + break; + } + } } if (contained) { - res->add_rule(r); + if (new_tail) { + rule* new_r = m_context.get_rule_manager().mk(r->get_head(), m_new_tail.size(), + m_new_tail.c_ptr(), m_new_tail_neg.c_ptr(), symbol::null, false); + res->add_rule(new_r); + } else { + res->add_rule(r); + } } + m_new_tail.reset(); + m_new_tail_neg.reset(); } if (res->get_num_rules() == source.get_num_rules()) { TRACE("dl", tout << "No transformation\n";); res = 0; - } - else { + } else { res->close(); } // set to false each unreached predicate if (m_context.get_model_converter()) { extension_model_converter* mc0 = alloc(extension_model_converter, m); - for (func_decl_set::iterator it = all.begin(); it != all.end(); ++it) { - if (!reached.contains(*it)) { - mc0->insert(*it, m.mk_false()); + for (dataflow_engine::iterator it = engine.begin(); it != engine.end(); it++) { + if (!it->m_value.is_reachable()) { + mc0->insert(it->m_key, m.mk_false()); } - } + } m_context.add_model_converter(mc0); } - // clean up body2rules range resources - for (rule_set::decl2rules::iterator it = body2rules.begin(); it != body2rules.end(); ++it) { - dealloc(it->m_value); - } CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } rule_set * mk_coi_filter::top_down(rule_set const & source) { - - func_decl_set interesting_preds; func_decl_set pruned_preds; - ptr_vector todo; - { - const func_decl_set& output_preds = source.get_output_predicates(); - func_decl_set::iterator oend = output_preds.end(); - for (func_decl_set::iterator it = output_preds.begin(); it!=oend; ++it) { - todo.push_back(*it); - interesting_preds.insert(*it); - } - } - - const rule_dependencies& deps = source.get_dependencies(); - - while (!todo.empty()) { - func_decl * curr = todo.back(); - todo.pop_back(); - interesting_preds.insert(curr); - - const rule_dependencies::item_set& cdeps = deps.get_deps(curr); - rule_dependencies::item_set::iterator dend = cdeps.end(); - for (rule_dependencies::item_set::iterator it = cdeps.begin(); it != dend; ++it) { - func_decl * dep_pred = *it; - if (!interesting_preds.contains(dep_pred)) { - interesting_preds.insert(dep_pred); - todo.push_back(dep_pred); - } - } - } - + dataflow_engine engine(source.get_manager(), source); + engine.run_top_down(); scoped_ptr res = alloc(rule_set, m_context); res->inherit_predicates(source); @@ -182,10 +106,9 @@ namespace datalog { for (rule_set::iterator rit = source.begin(); rit != rend; ++rit) { rule * r = *rit; func_decl * pred = r->get_decl(); - if (interesting_preds.contains(pred)) { + if (engine.get_fact(pred).is_reachable()) { res->add_rule(r); - } - else if (m_context.get_model_converter()) { + } else if (m_context.get_model_converter()) { pruned_preds.insert(pred); } } @@ -194,7 +117,6 @@ namespace datalog { TRACE("dl", tout << "No transformation\n";); res = 0; } - if (res && m_context.get_model_converter()) { func_decl_set::iterator end = pruned_preds.end(); func_decl_set::iterator it = pruned_preds.begin(); @@ -210,18 +132,16 @@ namespace datalog { if (!is_var(arg)) { conj.push_back(m.mk_eq(m.mk_var(j, m.get_sort(arg)), arg)); } - } + } fmls.push_back(m.mk_and(conj.size(), conj.c_ptr())); } expr_ref fml(m); fml = m.mk_or(fmls.size(), fmls.c_ptr()); mc0->insert(*it, fml); - } + } m_context.add_model_converter(mc0); } CTRACE("dl", 0 != res, res->display(tout);); return res.detach(); } - -}; - +} diff --git a/src/muz/transforms/dl_mk_coi_filter.h b/src/muz/transforms/dl_mk_coi_filter.h index 8ec7e80c4..16abe2f52 100644 --- a/src/muz/transforms/dl_mk_coi_filter.h +++ b/src/muz/transforms/dl_mk_coi_filter.h @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2006 Microsoft Corporation +Copyright (c) 2006-2015 Microsoft Corporation Module Name: @@ -7,22 +7,21 @@ Module Name: Abstract: - Rule transformer which removes relations which are out of the cone of + Rule transformer which removes relations which are out of the cone of influence of output relations Author: - - Krystof Hoder (t-khoder) 2011-10-01. - -Revision History: + Krystof Hoder (t-khoder) + Andrey Rybalchenko (rybal) + Henning Guenther (t-hennig) --*/ -#ifndef _DL_MK_COI_FILTER_H_ -#define _DL_MK_COI_FILTER_H_ +#ifndef DL_MK_COI_FILTER_H_ +#define DL_MK_COI_FILTER_H_ -#include "dl_context.h" #include "dl_rule_transformer.h" +#include "dl_context.h" namespace datalog { @@ -32,20 +31,19 @@ namespace datalog { ast_manager & m; context & m_context; - + vector m_new_tail; + svector m_new_tail_neg; rule_set * bottom_up(rule_set const & source); rule_set * top_down(rule_set const & source); public: - mk_coi_filter(context & ctx, unsigned priority=45000) + mk_coi_filter(context & ctx, unsigned priority = 45000) : plugin(priority), m(ctx.get_manager()), m_context(ctx) {} rule_set * operator()(rule_set const & source); }; +} -}; - -#endif /* _DL_MK_COI_FILTER_H_ */ - +#endif diff --git a/src/muz/transforms/dl_mk_different.h b/src/muz/transforms/dl_mk_different.h index 2def011f9..47a00edc0 100644 --- a/src/muz/transforms/dl_mk_different.h +++ b/src/muz/transforms/dl_mk_different.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_DIFFERENT_SYMBOLIC_H_ -#define _DL_MK_DIFFERENT_SYMBOLIC_H_ +#ifndef DL_MK_DIFFERENT_SYMBOLIC_H_ +#define DL_MK_DIFFERENT_SYMBOLIC_H_ #include"dl_rule_transformer.h" @@ -34,5 +34,5 @@ namespace datalog { }; -#endif /* _DL_MK_DIFFERENT_SYMBOLIC_H_ */ +#endif /* DL_MK_DIFFERENT_SYMBOLIC_H_ */ diff --git a/src/muz/transforms/dl_mk_filter_rules.cpp b/src/muz/transforms/dl_mk_filter_rules.cpp index ef89c9ffa..d23da72fb 100644 --- a/src/muz/transforms/dl_mk_filter_rules.cpp +++ b/src/muz/transforms/dl_mk_filter_rules.cpp @@ -79,13 +79,13 @@ namespace datalog { filter_key * key = alloc(filter_key, m); mk_new_rule_tail(m, pred, non_local_vars, filter_domain, key->filter_args, key->new_pred); - func_decl * filter_decl = 0; - if (!m_tail2filter.find(key, filter_decl)) { + filter_cache::obj_map_entry *entry = m_tail2filter.insert_if_not_there2(key, 0); + func_decl*& filter_decl = entry->get_data().m_value; + if (!filter_decl) { filter_decl = m_context.mk_fresh_head_predicate(pred->get_decl()->get_name(), symbol("filter"), filter_domain.size(), filter_domain.c_ptr(), pred->get_decl()); m_pinned.push_back(filter_decl); - m_tail2filter.insert(key, filter_decl); app_ref filter_head(m); filter_head = m.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); app * filter_tail = key->new_pred; diff --git a/src/muz/transforms/dl_mk_filter_rules.h b/src/muz/transforms/dl_mk_filter_rules.h index b51cb8e24..60ee80e90 100644 --- a/src/muz/transforms/dl_mk_filter_rules.h +++ b/src/muz/transforms/dl_mk_filter_rules.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_FILTER_RULES_H_ -#define _DL_MK_FILTER_RULES_H_ +#ifndef DL_MK_FILTER_RULES_H_ +#define DL_MK_FILTER_RULES_H_ #include"map.h" @@ -83,5 +83,5 @@ namespace datalog { }; -#endif /* _DL_MK_FILTER_RULES_H_ */ +#endif /* DL_MK_FILTER_RULES_H_ */ diff --git a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp index b01b4326c..ea0e6c887 100644 --- a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp @@ -25,6 +25,7 @@ Revision History: #include"rewriter_def.h" #include"dl_mk_rule_inliner.h" #include"dl_mk_interp_tail_simplifier.h" +#include"ast_util.h" namespace datalog { @@ -491,9 +492,10 @@ namespace datalog { bool mk_interp_tail_simplifier::transform_rule(rule * r0, rule_ref & res) { - rule_ref r(r0, m_context.get_rule_manager()); + rule_manager& rm = m_context.get_rule_manager(); + rule_ref r(r0, rm); - if (r->has_quantifiers()) { + if (rm.has_quantifiers(*r)) { res = r; return true; } @@ -546,7 +548,7 @@ namespace datalog { if (modified) { m_conj.reset(); - qe::flatten_and(simp_res, m_conj); + flatten_and(simp_res, m_conj); for (unsigned i = 0; i < m_conj.size(); ++i) { expr* e = m_conj[i].get(); if (is_app(e)) { diff --git a/src/muz/transforms/dl_mk_interp_tail_simplifier.h b/src/muz/transforms/dl_mk_interp_tail_simplifier.h index 5047e1c6e..568fcff3f 100644 --- a/src/muz/transforms/dl_mk_interp_tail_simplifier.h +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ -#define _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ +#ifndef DL_MK_INTERP_TAIL_SIMPLIFIER_H_ +#define DL_MK_INTERP_TAIL_SIMPLIFIER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" @@ -105,5 +105,5 @@ namespace datalog { }; -#endif /* _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */ +#endif /* DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */ diff --git a/src/muz/transforms/dl_mk_karr_invariants.h b/src/muz/transforms/dl_mk_karr_invariants.h index 378a7e587..ed43e550b 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.h +++ b/src/muz/transforms/dl_mk_karr_invariants.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_KARR_INVARIANTS_H_ -#define _DL_MK_KARR_INVARIANTS_H_ +#ifndef DL_MK_KARR_INVARIANTS_H_ +#define DL_MK_KARR_INVARIANTS_H_ #include"dl_context.h" #include"dl_rule_set.h" @@ -77,5 +77,5 @@ namespace datalog { }; -#endif /* _DL_MK_KARR_INVARIANTS_H_ */ +#endif /* DL_MK_KARR_INVARIANTS_H_ */ diff --git a/src/muz/transforms/dl_mk_loop_counter.h b/src/muz/transforms/dl_mk_loop_counter.h index d67c88e0e..6694ffb52 100644 --- a/src/muz/transforms/dl_mk_loop_counter.h +++ b/src/muz/transforms/dl_mk_loop_counter.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_LOOP_COUNTER_H_ -#define _DL_MK_LOOP_COUNTER_H_ +#ifndef DL_MK_LOOP_COUNTER_H_ +#define DL_MK_LOOP_COUNTER_H_ #include"dl_rule_transformer.h" #include"arith_decl_plugin.h" @@ -47,5 +47,5 @@ namespace datalog { }; -#endif /* _DL_MK_LOOP_COUNTER_H_ */ +#endif /* DL_MK_LOOP_COUNTER_H_ */ diff --git a/src/muz/transforms/dl_mk_magic_sets.h b/src/muz/transforms/dl_mk_magic_sets.h index 3496a5967..f71af3649 100644 --- a/src/muz/transforms/dl_mk_magic_sets.h +++ b/src/muz/transforms/dl_mk_magic_sets.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_MAGIC_SETS_H_ -#define _DL_MK_MAGIC_SETS_H_ +#ifndef DL_MK_MAGIC_SETS_H_ +#define DL_MK_MAGIC_SETS_H_ #include @@ -131,5 +131,5 @@ namespace datalog { }; -#endif /* _DL_MK_MAGIC_SETS_H_ */ +#endif /* DL_MK_MAGIC_SETS_H_ */ diff --git a/src/muz/transforms/dl_mk_magic_symbolic.h b/src/muz/transforms/dl_mk_magic_symbolic.h index ce9ca1145..db3137c90 100644 --- a/src/muz/transforms/dl_mk_magic_symbolic.h +++ b/src/muz/transforms/dl_mk_magic_symbolic.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_MAGIC_SYMBOLIC_H_ -#define _DL_MK_MAGIC_SYMBOLIC_H_ +#ifndef DL_MK_MAGIC_SYMBOLIC_H_ +#define DL_MK_MAGIC_SYMBOLIC_H_ #include"dl_rule_transformer.h" @@ -36,5 +36,5 @@ namespace datalog { }; -#endif /* _DL_MK_MAGIC_SYMBOLIC_H_ */ +#endif /* DL_MK_MAGIC_SYMBOLIC_H_ */ diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.h b/src/muz/transforms/dl_mk_quantifier_abstraction.h index 0e65c54f7..d4a588127 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.h +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.h @@ -22,8 +22,8 @@ Revision History: "On Solving Universally Quantified Horn Clauses" --*/ -#ifndef _DL_MK_QUANTIFIER_ABSTRACTION_H_ -#define _DL_MK_QUANTIFIER_ABSTRACTION_H_ +#ifndef DL_MK_QUANTIFIER_ABSTRACTION_H_ +#define DL_MK_QUANTIFIER_ABSTRACTION_H_ #include"dl_rule_transformer.h" @@ -60,5 +60,5 @@ namespace datalog { }; -#endif /* _DL_MK_QUANTIFIER_ABSTRACTION_H_ */ +#endif /* DL_MK_QUANTIFIER_ABSTRACTION_H_ */ diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index fcb8d7b85..5d36d26cc 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -49,7 +49,7 @@ namespace datalog { for (unsigned j = 0; j < tsz; ++j) { conjs.push_back(r.get_tail(j)); } - qe::flatten_and(conjs); + flatten_and(conjs); for (unsigned j = 0; j < conjs.size(); ++j) { expr* e = conjs[j].get(); quantifier* q; @@ -238,7 +238,7 @@ namespace datalog { proof* p1 = r.get_proof(); for (unsigned i = 0; i < added_rules.get_num_rules(); ++i) { rule* r2 = added_rules.get_rule(i); - r2->to_formula(fml); + rm.to_formula(*r2, fml); pr = m.mk_modus_ponens(m.mk_def_axiom(m.mk_implies(m.get_fact(p1), fml)), p1); r2->set_proof(m, pr); } @@ -252,9 +252,10 @@ namespace datalog { } bool has_quantifiers = false; unsigned sz = source.get_num_rules(); + rule_manager& rm = m_ctx.get_rule_manager(); for (unsigned i = 0; !has_quantifiers && i < sz; ++i) { rule& r = *source.get_rule(i); - has_quantifiers = has_quantifiers || r.has_quantifiers(); + has_quantifiers = has_quantifiers || rm.has_quantifiers(r); if (r.has_negation()) { return 0; } diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.h b/src/muz/transforms/dl_mk_quantifier_instantiation.h index 138d5abee..b9ef5189e 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.h +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.h @@ -22,12 +22,13 @@ Revision History: "On Solving Universally Quantified Horn Clauses" --*/ -#ifndef _DL_MK_QUANTIFIER_INSTANTIATION_H_ -#define _DL_MK_QUANTIFIER_INSTANTIATION_H_ +#ifndef DL_MK_QUANTIFIER_INSTANTIATION_H_ +#define DL_MK_QUANTIFIER_INSTANTIATION_H_ -#include"dl_rule_transformer.h" -#include"expr_safe_replace.h" +#include "dl_rule_transformer.h" +#include "expr_safe_replace.h" +#include "union_find.h" namespace datalog { @@ -37,75 +38,12 @@ namespace datalog { class mk_quantifier_instantiation : public rule_transformer::plugin { typedef svector > term_pairs; - class union_find { - unsigned_vector m_find; - unsigned_vector m_size; - unsigned_vector m_next; - - void ensure_size(unsigned v) { - while (v >= get_num_vars()) { - mk_var(); - } - } - public: - unsigned mk_var() { - unsigned r = m_find.size(); - m_find.push_back(r); - m_size.push_back(1); - m_next.push_back(r); - return r; - } - unsigned get_num_vars() const { return m_find.size(); } - - unsigned find(unsigned v) const { - if (v >= get_num_vars()) { - return v; - } - while (true) { - unsigned new_v = m_find[v]; - if (new_v == v) - return v; - v = new_v; - } - } - - unsigned next(unsigned v) const { - if (v >= get_num_vars()) { - return v; - } - return m_next[v]; - } - - bool is_root(unsigned v) const { - return v >= get_num_vars() || m_find[v] == v; - } - - void merge(unsigned v1, unsigned v2) { - unsigned r1 = find(v1); - unsigned r2 = find(v2); - if (r1 == r2) - return; - ensure_size(v1); - ensure_size(v2); - if (m_size[r1] > m_size[r2]) - std::swap(r1, r2); - m_find[r1] = r2; - m_size[r2] += m_size[r1]; - std::swap(m_next[r1], m_next[r2]); - } - - void reset() { - m_find.reset(); - m_next.reset(); - m_size.reset(); - } - }; ast_manager& m; context& m_ctx; expr_safe_replace m_var2cnst; expr_safe_replace m_cnst2var; - union_find m_uf; + basic_union_find m_uf; ptr_vector m_todo; ptr_vector m_terms; ptr_vector m_binding; @@ -132,5 +70,5 @@ namespace datalog { }; -#endif /* _DL_MK_QUANTIFIER_INSTANTIATION_H_ */ +#endif /* DL_MK_QUANTIFIER_INSTANTIATION_H_ */ diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index 522bd2e86..7dabece2f 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -179,7 +179,7 @@ namespace datalog { if (m_context.generate_proof_trace()) { expr_ref_vector s1 = m_unifier.get_rule_subst(tgt, true); expr_ref_vector s2 = m_unifier.get_rule_subst(src, false); - datalog::resolve_rule(tgt, src, tail_index, s1, s2, *res.get()); + datalog::resolve_rule(m_rm, tgt, src, tail_index, s1, s2, *res.get()); } return true; } @@ -644,7 +644,8 @@ namespace datalog { tout << " num unifiers: " << m_unifiers.size(); tout << " num positions: " << m_positions.find(e).size() << "\n"; output_predicate(m_context, to_app(e), tout); tout << "\n";); - return true; + // stop visitor when we have more than 1 unifier, since that's all we want. + return m_unifiers.size() <= 1; } void mk_rule_inliner::visitor::reset(unsigned sz) { @@ -754,7 +755,7 @@ namespace datalog { valid.reset(); valid.resize(sz, true); - bool allow_branching = m_context.get_params().inline_linear_branch(); + bool allow_branching = m_context.get_params().xform_inline_linear_branch(); for (unsigned i = 0; i < sz; ++i) { @@ -866,7 +867,7 @@ namespace datalog { scoped_ptr res = alloc(rule_set, m_context); - if (m_context.get_params().inline_eager()) { + if (m_context.get_params().xform_inline_eager()) { TRACE("dl", source.display(tout << "before eager inlining\n");); plan_inlining(source); something_done = transform_rules(source, *res); @@ -884,7 +885,7 @@ namespace datalog { res = alloc(rule_set, source); } - if (m_context.get_params().inline_linear() && inline_linear(res)) { + if (m_context.get_params().xform_inline_linear() && inline_linear(res)) { something_done = true; } diff --git a/src/muz/transforms/dl_mk_rule_inliner.h b/src/muz/transforms/dl_mk_rule_inliner.h index ed555492a..98f65a734 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.h +++ b/src/muz/transforms/dl_mk_rule_inliner.h @@ -17,8 +17,8 @@ Revision History: --*/ -#ifndef _DL_MK_RULE_INLINER_H_ -#define _DL_MK_RULE_INLINER_H_ +#ifndef DL_MK_RULE_INLINER_H_ +#define DL_MK_RULE_INLINER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" @@ -201,5 +201,5 @@ namespace datalog { }; -#endif /* _DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */ +#endif /* DL_MK_INTERP_TAIL_SIMPLIFIER_H_ */ diff --git a/src/muz/transforms/dl_mk_scale.h b/src/muz/transforms/dl_mk_scale.h index 8498c891f..0e4ccf7ea 100644 --- a/src/muz/transforms/dl_mk_scale.h +++ b/src/muz/transforms/dl_mk_scale.h @@ -18,8 +18,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_SCALE_H_ -#define _DL_MK_SCALE_H_ +#ifndef DL_MK_SCALE_H_ +#define DL_MK_SCALE_H_ #include"dl_rule_transformer.h" #include"arith_decl_plugin.h" @@ -49,5 +49,5 @@ namespace datalog { }; -#endif /* _DL_MK_SCALE_H_ */ +#endif /* DL_MK_SCALE_H_ */ diff --git a/src/muz/transforms/dl_mk_separate_negated_tails.cpp b/src/muz/transforms/dl_mk_separate_negated_tails.cpp index 782e1011d..9a78c0d4d 100644 --- a/src/muz/transforms/dl_mk_separate_negated_tails.cpp +++ b/src/muz/transforms/dl_mk_separate_negated_tails.cpp @@ -37,10 +37,10 @@ namespace datalog { void mk_separate_negated_tails::get_private_vars(rule const& r, unsigned j) { m_vars.reset(); m_fv.reset(); - get_free_vars(r.get_head(), m_fv); + m_fv(r.get_head()); for (unsigned i = 0; i < r.get_tail_size(); ++i) { if (i != j) { - get_free_vars(r.get_tail(i), m_fv); + m_fv.accumulate(r.get_tail(i)); } } @@ -49,7 +49,7 @@ namespace datalog { expr* v = p->get_arg(i); if (is_var(v)) { unsigned idx = to_var(v)->get_idx(); - if (idx >= m_fv.size() || !m_fv[idx]) { + if (!m_fv.contains(idx)) { m_vars.push_back(v); } } diff --git a/src/muz/transforms/dl_mk_separate_negated_tails.h b/src/muz/transforms/dl_mk_separate_negated_tails.h index 8cd806f43..1d2e57e2c 100644 --- a/src/muz/transforms/dl_mk_separate_negated_tails.h +++ b/src/muz/transforms/dl_mk_separate_negated_tails.h @@ -29,8 +29,8 @@ Revision History: --*/ -#ifndef _DL_MK_SEPARAT_NEGATED_TAILS_H_ -#define _DL_MK_SEPARAT_NEGATED_TAILS_H_ +#ifndef DL_MK_SEPARAT_NEGATED_TAILS_H_ +#define DL_MK_SEPARAT_NEGATED_TAILS_H_ #include "dl_rule_transformer.h" #include "dl_context.h" @@ -42,7 +42,7 @@ namespace datalog { rule_manager& rm; context & m_ctx; ptr_vector m_vars; - ptr_vector m_fv; + expr_free_vars m_fv; bool has_private_vars(rule const& r, unsigned j); void get_private_vars(rule const& r, unsigned j); diff --git a/src/muz/transforms/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp index 0df75324d..2f7d0d475 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -52,6 +52,7 @@ Revision History: #include "dl_mk_slice.h" #include "ast_pp.h" +#include "ast_util.h" #include "expr_functors.h" #include "dl_mk_rule_inliner.h" #include "model_smt2_pp.h" @@ -120,7 +121,7 @@ namespace datalog { obj_map::iterator end = m_rule2slice.end(); expr_ref fml(m); for (; it != end; ++it) { - it->m_value->to_formula(fml); + rm.to_formula(*it->m_value, fml); m_pinned_exprs.push_back(fml); TRACE("dl", tout << "orig: " << mk_pp(fml, m) << "\n"; @@ -238,7 +239,7 @@ namespace datalog { r3->display(m_ctx, tout << "res:");); r1 = r3; } - r1->to_formula(concl); + rm.to_formula(*r1.get(), concl); proof* new_p = m.mk_hyper_resolve(premises.size(), premises.c_ptr(), concl, positions, substs); m_pinned_exprs.push_back(new_p); m_pinned_rules.push_back(r1.get()); @@ -619,7 +620,7 @@ namespace datalog { for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { conjs.push_back(r.get_tail(j)); } - qe::flatten_and(conjs); + flatten_and(conjs); return conjs; } @@ -676,10 +677,10 @@ namespace datalog { } void mk_slice::add_free_vars(uint_set& result, expr* e) { - ptr_vector sorts; - get_free_vars(e, sorts); - for (unsigned i = 0; i < sorts.size(); ++i) { - if (sorts[i]) { + expr_free_vars fv; + fv(e); + for (unsigned i = 0; i < fv.size(); ++i) { + if (fv[i]) { result.insert(i); } } @@ -773,14 +774,11 @@ namespace datalog { init_vars(r); app_ref_vector tail(m); app_ref head(m); - ptr_vector sorts; update_predicate(r.get_head(), head); - get_free_vars(head.get(), sorts); for (unsigned i = 0; i < r.get_uninterpreted_tail_size(); ++i) { app_ref t(m); update_predicate(r.get_tail(i), t); tail.push_back(t); - get_free_vars(t, sorts); } expr_ref_vector conjs = get_tail_conjs(r); @@ -816,9 +814,10 @@ namespace datalog { } } - rule_set * mk_slice::operator()(rule_set const & src) { + rule_set * mk_slice::operator()(rule_set const & src) { + rule_manager& rm = m_ctx.get_rule_manager(); for (unsigned i = 0; i < src.get_num_rules(); ++i) { - if (src.get_rule(i)->has_quantifiers()) { + if (rm.has_quantifiers(*src.get_rule(i))) { return 0; } } diff --git a/src/muz/transforms/dl_mk_slice.h b/src/muz/transforms/dl_mk_slice.h index aedc1feb0..b1b9cb46e 100644 --- a/src/muz/transforms/dl_mk_slice.h +++ b/src/muz/transforms/dl_mk_slice.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_SLICE_H_ -#define _DL_MK_SLICE_H_ +#ifndef DL_MK_SLICE_H_ +#define DL_MK_SLICE_H_ #include"dl_context.h" #include"dl_rule_set.h" @@ -111,5 +111,5 @@ namespace datalog { }; -#endif /* _DL_MK_SLICE_H_ */ +#endif /* DL_MK_SLICE_H_ */ diff --git a/src/muz/transforms/dl_mk_subsumption_checker.h b/src/muz/transforms/dl_mk_subsumption_checker.h index 8d797a9e2..270418508 100644 --- a/src/muz/transforms/dl_mk_subsumption_checker.h +++ b/src/muz/transforms/dl_mk_subsumption_checker.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef _DL_MK_SUBSUMPTION_CHECKER_H_ -#define _DL_MK_SUBSUMPTION_CHECKER_H_ +#ifndef DL_MK_SUBSUMPTION_CHECKER_H_ +#define DL_MK_SUBSUMPTION_CHECKER_H_ #include "dl_context.h" #include "dl_rule_transformer.h" @@ -89,5 +89,5 @@ namespace datalog { }; -#endif /* _DL_MK_SUBSUMPTION_CHECKER_H_ */ +#endif /* DL_MK_SUBSUMPTION_CHECKER_H_ */ diff --git a/src/muz/transforms/dl_mk_unbound_compressor.cpp b/src/muz/transforms/dl_mk_unbound_compressor.cpp index 68c35e2c9..99a3b80f9 100644 --- a/src/muz/transforms/dl_mk_unbound_compressor.cpp +++ b/src/muz/transforms/dl_mk_unbound_compressor.cpp @@ -93,7 +93,7 @@ namespace datalog { unsigned n = head_pred->get_arity(); rm.get_counter().reset(); - rm.get_counter().count_vars(m, head, 1); + rm.get_counter().count_vars(head, 1); for (unsigned i=0; iget_arg(i); @@ -125,7 +125,7 @@ namespace datalog { unsigned head_arity = head_pred->get_arity(); rm.get_counter().reset(); - rm.get_counter().count_vars(m, head); + rm.get_counter().count_vars(head); unsigned arg_index; for (arg_index = 0; arg_index < head_arity; arg_index++) { diff --git a/src/muz/transforms/dl_mk_unbound_compressor.h b/src/muz/transforms/dl_mk_unbound_compressor.h index 44877e646..889ded058 100644 --- a/src/muz/transforms/dl_mk_unbound_compressor.h +++ b/src/muz/transforms/dl_mk_unbound_compressor.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_UNBOUND_COMPRESSOR_H_ -#define _DL_MK_UNBOUND_COMPRESSOR_H_ +#ifndef DL_MK_UNBOUND_COMPRESSOR_H_ +#define DL_MK_UNBOUND_COMPRESSOR_H_ #include @@ -88,5 +88,5 @@ namespace datalog { }; -#endif /* _DL_MK_UNBOUND_COMPRESSOR_H_ */ +#endif /* DL_MK_UNBOUND_COMPRESSOR_H_ */ diff --git a/src/muz/transforms/dl_mk_unfold.cpp b/src/muz/transforms/dl_mk_unfold.cpp index a9357f88a..cc460bca1 100644 --- a/src/muz/transforms/dl_mk_unfold.cpp +++ b/src/muz/transforms/dl_mk_unfold.cpp @@ -43,7 +43,7 @@ namespace datalog { m_unify.apply(r, tail_idx, r2, new_rule)) { expr_ref_vector s1 = m_unify.get_rule_subst(r, true); expr_ref_vector s2 = m_unify.get_rule_subst(r2, false); - resolve_rule(r, r2, tail_idx, s1, s2, *new_rule.get()); + resolve_rule(rm, r, r2, tail_idx, s1, s2, *new_rule.get()); expand_tail(*new_rule.get(), tail_idx+r2.get_uninterpreted_tail_size(), src, dst); } } diff --git a/src/muz/transforms/dl_mk_unfold.h b/src/muz/transforms/dl_mk_unfold.h index 26f64926d..554ed69e0 100644 --- a/src/muz/transforms/dl_mk_unfold.h +++ b/src/muz/transforms/dl_mk_unfold.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DL_MK_UNFOLD_H_ -#define _DL_MK_UNFOLD_H_ +#ifndef DL_MK_UNFOLD_H_ +#define DL_MK_UNFOLD_H_ #include"dl_context.h" #include"dl_rule_set.h" @@ -49,5 +49,5 @@ namespace datalog { }; -#endif /* _DL_MK_UNFOLD_H_ */ +#endif /* DL_MK_UNFOLD_H_ */ diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp index 08404b6e5..81f9d6f64 100644 --- a/src/muz/transforms/dl_transforms.cpp +++ b/src/muz/transforms/dl_transforms.cpp @@ -38,12 +38,19 @@ Revision History: namespace datalog { void apply_default_transformation(context& ctx) { + flet _enable_bv(ctx.bind_vars_enabled(), false); + rule_transformer transf(ctx); ctx.ensure_closed(); transf.reset(); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx)); + if (ctx.get_params().xform_quantify_arrays()) { + transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 38000)); + } + transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 37000)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 35000)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34990)); @@ -63,21 +70,15 @@ namespace datalog { transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34890)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880)); - - if (ctx.get_params().quantify_arrays()) { - transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 33000)); - transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 32500)); - } - transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 32000)); - transf.register_plugin(alloc(datalog::mk_bit_blast, ctx, 35000)); - if (!ctx.get_params().quantify_arrays()) - transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000)); transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010)); - if (ctx.get_params().magic()) { + transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030)); + if (!ctx.get_params().xform_quantify_arrays()) { + transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000)); + } + if (ctx.get_params().xform_magic()) { transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020)); } - transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030)); ctx.transform_rules(transf); } } diff --git a/src/muz/transforms/dl_transforms.h b/src/muz/transforms/dl_transforms.h index 4f9a92dd8..71131c99f 100644 --- a/src/muz/transforms/dl_transforms.h +++ b/src/muz/transforms/dl_transforms.h @@ -18,8 +18,8 @@ Revision History: Extracted from dl_context --*/ -#ifndef _DL_TRANSFORMS_H_ -#define _DL_TRANSFORMS_H_ +#ifndef DL_TRANSFORMS_H_ +#define DL_TRANSFORMS_H_ #include "dl_context.h" diff --git a/src/nlsat/nlsat_assignment.h b/src/nlsat/nlsat_assignment.h index 5169d5fb6..178a7f738 100644 --- a/src/nlsat/nlsat_assignment.h +++ b/src/nlsat/nlsat_assignment.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _NLSAT_ASSIGNMENT_H_ -#define _NLSAT_ASSIGNMENT_H_ +#ifndef NLSAT_ASSIGNMENT_H_ +#define NLSAT_ASSIGNMENT_H_ #include"nlsat_types.h" #include"algebraic_numbers.h" diff --git a/src/nlsat/nlsat_clause.h b/src/nlsat/nlsat_clause.h index 549cb69fa..95928eb1a 100644 --- a/src/nlsat/nlsat_clause.h +++ b/src/nlsat/nlsat_clause.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _NLSAT_CLAUSE_H_ -#define _NLSAT_CLAUSE_H_ +#ifndef NLSAT_CLAUSE_H_ +#define NLSAT_CLAUSE_H_ #include"nlsat_types.h" #include"vector.h" diff --git a/src/nlsat/nlsat_evaluator.h b/src/nlsat/nlsat_evaluator.h index 166416902..0762c5360 100644 --- a/src/nlsat/nlsat_evaluator.h +++ b/src/nlsat/nlsat_evaluator.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _NLSAT_EVALUATOR_H_ -#define _NLSAT_EVALUATOR_H_ +#ifndef NLSAT_EVALUATOR_H_ +#define NLSAT_EVALUATOR_H_ #include"nlsat_types.h" #include"nlsat_assignment.h" diff --git a/src/nlsat/nlsat_explain.h b/src/nlsat/nlsat_explain.h index 85c598f73..ac99b434c 100644 --- a/src/nlsat/nlsat_explain.h +++ b/src/nlsat/nlsat_explain.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _NLSAT_EXPLAIN_H_ -#define _NLSAT_EXPLAIN_H_ +#ifndef NLSAT_EXPLAIN_H_ +#define NLSAT_EXPLAIN_H_ #include"nlsat_solver.h" #include"nlsat_scoped_literal_vector.h" diff --git a/src/nlsat/nlsat_interval_set.h b/src/nlsat/nlsat_interval_set.h index 70822f405..a308c8637 100644 --- a/src/nlsat/nlsat_interval_set.h +++ b/src/nlsat/nlsat_interval_set.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _NLSAT_INTERVAL_SET_H_ -#define _NLSAT_INTERVAL_SET_H_ +#ifndef NLSAT_INTERVAL_SET_H_ +#define NLSAT_INTERVAL_SET_H_ #include"nlsat_types.h" diff --git a/src/nlsat/nlsat_justification.h b/src/nlsat/nlsat_justification.h index 4422f48d2..b226dc522 100644 --- a/src/nlsat/nlsat_justification.h +++ b/src/nlsat/nlsat_justification.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _NLSAT_JUSTIFICATION_H_ -#define _NLSAT_JUSTIFICATION_H_ +#ifndef NLSAT_JUSTIFICATION_H_ +#define NLSAT_JUSTIFICATION_H_ #include"nlsat_types.h" #include"tptr.h" diff --git a/src/nlsat/nlsat_scoped_literal_vector.h b/src/nlsat/nlsat_scoped_literal_vector.h index e76110d5d..6ec7a3461 100644 --- a/src/nlsat/nlsat_scoped_literal_vector.h +++ b/src/nlsat/nlsat_scoped_literal_vector.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _NLSAT_SCOPED_LITERAL_VECTOR_H_ -#define _NLSAT_SCOPED_LITERAL_VECTOR_H_ +#ifndef NLSAT_SCOPED_LITERAL_VECTOR_H_ +#define NLSAT_SCOPED_LITERAL_VECTOR_H_ #include"nlsat_solver.h" diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 368e7eb83..6ba831cb1 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -67,6 +67,7 @@ namespace nlsat { typedef ptr_vector interval_set_vector; solver & m_solver; + reslimit& m_rlimit; small_object_allocator m_allocator; unsynch_mpq_manager m_qm; pmanager m_pm; @@ -159,8 +160,9 @@ namespace nlsat { unsigned m_stages; unsigned m_irrational_assignments; // number of irrational witnesses - imp(solver & s, params_ref const & p): + imp(solver & s, reslimit& rlim, params_ref const & p): m_solver(s), + m_rlimit(rlim), m_allocator("nlsat"), m_pm(m_qm, &m_allocator), m_cache(m_pm), @@ -224,6 +226,7 @@ namespace nlsat { void checkpoint() { if (m_cancel) throw solver_exception(Z3_CANCELED_MSG); + if (!m_rlimit.inc()) throw solver_exception(Z3_MAX_RESOURCE_MSG); if (memory::get_allocation_size() > m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } @@ -2556,8 +2559,8 @@ namespace nlsat { } }; - solver::solver(params_ref const & p) { - m_imp = alloc(imp, *this, p); + solver::solver(reslimit& rlim, params_ref const & p) { + m_imp = alloc(imp, *this, rlim, p); } solver::~solver() { diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index dcee46f05..d99f4371a 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -18,12 +18,13 @@ Author: Revision History: --*/ -#ifndef _NLSAT_SOLVER_H_ -#define _NLSAT_SOLVER_H_ +#ifndef NLSAT_SOLVER_H_ +#define NLSAT_SOLVER_H_ #include"nlsat_types.h" #include"params.h" #include"statistics.h" +#include"rlimit.h" namespace nlsat { @@ -31,7 +32,7 @@ namespace nlsat { struct imp; imp * m_imp; public: - solver(params_ref const & p); + solver(reslimit& rlim, params_ref const & p); ~solver(); /** diff --git a/src/nlsat/nlsat_types.h b/src/nlsat/nlsat_types.h index cd3a61a10..e6ab30c19 100644 --- a/src/nlsat/nlsat_types.h +++ b/src/nlsat/nlsat_types.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _NLSAT_TYPES_H_ -#define _NLSAT_TYPES_H_ +#ifndef NLSAT_TYPES_H_ +#define NLSAT_TYPES_H_ #include"polynomial.h" #include"buffer.h" diff --git a/src/nlsat/tactic/goal2nlsat.h b/src/nlsat/tactic/goal2nlsat.h index ad84ef9b3..0d65b72d5 100644 --- a/src/nlsat/tactic/goal2nlsat.h +++ b/src/nlsat/tactic/goal2nlsat.h @@ -21,8 +21,8 @@ Author: Notes: --*/ -#ifndef _GOAL2NLSAT_H_ -#define _GOAL2NLSAT_H_ +#ifndef GOAL2NLSAT_H_ +#define GOAL2NLSAT_H_ #include"nlsat_types.h" #include"model_converter.h" diff --git a/src/nlsat/tactic/nlsat_tactic.cpp b/src/nlsat/tactic/nlsat_tactic.cpp index 868975965..0f2c80c29 100644 --- a/src/nlsat/tactic/nlsat_tactic.cpp +++ b/src/nlsat/tactic/nlsat_tactic.cpp @@ -50,7 +50,7 @@ class nlsat_tactic : public tactic { m(_m), m_params(p), m_display_var(_m), - m_solver(p) { + m_solver(m.limit(), p) { } void updt_params(params_ref const & p) { diff --git a/src/nlsat/tactic/nlsat_tactic.h b/src/nlsat/tactic/nlsat_tactic.h index afd63e01a..5f0022026 100644 --- a/src/nlsat/tactic/nlsat_tactic.h +++ b/src/nlsat/tactic/nlsat_tactic.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _NLSAT_TACTIC_H_ -#define _NLSAT_TACTIC_H_ +#ifndef NLSAT_TACTIC_H_ +#define NLSAT_TACTIC_H_ #include"params.h" class ast_manager; diff --git a/src/nlsat/tactic/qfnra_nlsat_tactic.h b/src/nlsat/tactic/qfnra_nlsat_tactic.h index e0633fed9..fa106ca93 100644 --- a/src/nlsat/tactic/qfnra_nlsat_tactic.h +++ b/src/nlsat/tactic/qfnra_nlsat_tactic.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _QFNRA_NLSAT_TACTIC_H_ -#define _QFNRA_NLSAT_TACTIC_H_ +#ifndef QFNRA_NLSAT_TACTIC_H_ +#define QFNRA_NLSAT_TACTIC_H_ #include"params.h" class ast_manager; diff --git a/src/opt/bcd2.cpp b/src/opt/bcd2.cpp new file mode 100644 index 000000000..e69275b27 --- /dev/null +++ b/src/opt/bcd2.cpp @@ -0,0 +1,406 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + bcd2.cpp + +Abstract: + + bcd2 based MaxSAT. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-17 + +Notes: + +--*/ +#include "bcd2.h" +#include "pb_decl_plugin.h" +#include "uint_set.h" +#include "ast_pp.h" + + +namespace opt { + // ------------------------------------------------------ + // Morgado, Heras, Marques-Silva 2013 + // (initial version without model-based optimizations) + // + class bcd2 : public maxsmt_solver_base { + struct wcore { + expr* m_r; + unsigned_vector m_R; + rational m_lower; + rational m_mid; + rational m_upper; + }; + typedef obj_hashtable expr_set; + + pb_util pb; + expr_ref_vector m_soft_aux; + obj_map m_relax2index; // expr |-> index + obj_map m_soft2index; // expr |-> index + expr_ref_vector m_trail; + expr_ref_vector m_soft_constraints; + expr_set m_asm_set; + vector m_cores; + vector m_sigmas; + rational m_den; // least common multiplier of original denominators + bool m_enable_lazy; // enable adding soft constraints lazily (called 'mgbcd2') + unsigned_vector m_lazy_soft; // soft constraints to add lazily. + + void set2asms(expr_set const& set, expr_ref_vector & es) const { + es.reset(); + expr_set::iterator it = set.begin(), end = set.end(); + for (; it != end; ++it) { + es.push_back(m.mk_not(*it)); + } + } + void bcd2_init_soft(weights_t& weights, expr_ref_vector const& soft) { + + // normalize weights to be integral: + m_den = rational::one(); + for (unsigned i = 0; i < m_weights.size(); ++i) { + m_den = lcm(m_den, denominator(m_weights[i])); + } + if (!m_den.is_one()) { + for (unsigned i = 0; i < m_weights.size(); ++i) { + m_weights[i] = m_den*m_weights[i]; + SASSERT(m_weights[i].is_int()); + } + } + } + void init_bcd() { + m_trail.reset(); + m_asm_set.reset(); + m_cores.reset(); + m_sigmas.reset(); + m_lazy_soft.reset(); + for (unsigned i = 0; i < m_soft.size(); ++i) { + m_sigmas.push_back(m_weights[i]); + m_soft_aux.push_back(mk_fresh()); + if (m_enable_lazy) { + m_lazy_soft.push_back(i); + } + else { + enable_soft_constraint(i); + } + } + m_upper += rational(1); + } + + void process_sat() { + svector assignment; + update_assignment(assignment); + if (check_lazy_soft(assignment)) { + update_sigmas(); + } + } + + public: + bcd2(maxsat_context& c, + weights_t& ws, expr_ref_vector const& soft): + maxsmt_solver_base(c, ws, soft), + pb(m), + m_soft_aux(m), + m_trail(m), + m_soft_constraints(m), + m_enable_lazy(true) { + bcd2_init_soft(ws, soft); + } + + virtual ~bcd2() {} + + virtual lbool operator()() { + expr_ref fml(m), r(m); + lbool is_sat = l_undef; + expr_ref_vector asms(m); + init(); + init_bcd(); + if (m_cancel) { + normalize_bounds(); + return l_undef; + } + process_sat(); + while (m_lower < m_upper) { + trace_bounds("bcd2"); + assert_soft(); + solver::scoped_push _scope2(s()); + TRACE("opt", display(tout);); + assert_cores(); + set2asms(m_asm_set, asms); + if (m_cancel) { + normalize_bounds(); + return l_undef; + } + is_sat = s().check_sat(asms.size(), asms.c_ptr()); + switch(is_sat) { + case l_undef: + normalize_bounds(); + return l_undef; + case l_true: + process_sat(); + break; + case l_false: { + ptr_vector unsat_core; + uint_set subC, soft; + s().get_unsat_core(unsat_core); + core2indices(unsat_core, subC, soft); + SASSERT(unsat_core.size() == subC.num_elems() + soft.num_elems()); + if (soft.num_elems() == 0 && subC.num_elems() == 1) { + unsigned s = *subC.begin(); + wcore& c_s = m_cores[s]; + c_s.m_lower = refine(c_s.m_R, c_s.m_mid); + c_s.m_mid = div(c_s.m_lower + c_s.m_upper, rational(2)); + } + else { + wcore c_s; + rational delta = min_of_delta(subC); + rational lower = sum_of_lower(subC); + union_Rs(subC, c_s.m_R); + r = mk_fresh(); + relax(subC, soft, c_s.m_R, delta); + c_s.m_lower = refine(c_s.m_R, lower + delta - rational(1)); + c_s.m_upper = rational::one(); + c_s.m_upper += sum_of_sigmas(c_s.m_R); + c_s.m_mid = div(c_s.m_lower + c_s.m_upper, rational(2)); + c_s.m_r = r; + m_asm_set.insert(r); + subtract(m_cores, subC); + m_relax2index.insert(r, m_cores.size()); + m_cores.push_back(c_s); + } + break; + } + } + m_lower = compute_lower(); + } + normalize_bounds(); + return l_true; + } + + + private: + + void enable_soft_constraint(unsigned i) { + expr_ref fml(m); + expr* r = m_soft_aux[i].get(); + m_soft2index.insert(r, i); + fml = m.mk_or(r, m_soft[i]); + m_soft_constraints.push_back(fml); + m_asm_set.insert(r); + SASSERT(m_weights[i].is_int()); + } + + void assert_soft() { + for (unsigned i = 0; i < m_soft_constraints.size(); ++i) { + s().assert_expr(m_soft_constraints[i].get()); + } + m_soft_constraints.reset(); + } + + bool check_lazy_soft(svector const& assignment) { + bool all_satisfied = true; + for (unsigned i = 0; i < m_lazy_soft.size(); ++i) { + unsigned j = m_lazy_soft[i]; + if (!assignment[j]) { + enable_soft_constraint(j); + m_lazy_soft[i] = m_lazy_soft.back(); + m_lazy_soft.pop_back(); + --i; + all_satisfied = false; + } + } + return all_satisfied; + } + + void normalize_bounds() { + m_lower /= m_den; + m_upper /= m_den; + } + + expr* mk_fresh() { + expr* r = mk_fresh_bool("r"); + m_trail.push_back(r); + return r; + } + + void update_assignment(svector& new_assignment) { + expr_ref val(m); + rational new_upper(0); + model_ref model; + new_assignment.reset(); + s().get_model(model); + for (unsigned i = 0; i < m_soft.size(); ++i) { + VERIFY(model->eval(m_soft[i], val)); + new_assignment.push_back(m.is_true(val)); + if (!new_assignment[i]) { + new_upper += m_weights[i]; + } + } + if (new_upper < m_upper) { + m_upper = new_upper; + m_model = model; + m_assignment.reset(); + m_assignment.append(new_assignment); + } + } + + void update_sigmas() { + for (unsigned i = 0; i < m_cores.size(); ++i) { + wcore& c_i = m_cores[i]; + unsigned_vector const& R = c_i.m_R; + c_i.m_upper.reset(); + for (unsigned j = 0; j < R.size(); ++j) { + unsigned r_j = R[j]; + if (!m_assignment[r_j]) { + c_i.m_upper += m_weights[r_j]; + m_sigmas[r_j] = m_weights[r_j]; + } + else { + m_sigmas[r_j].reset(); + } + } + c_i.m_mid = div(c_i.m_lower + c_i.m_upper, rational(2)); + } + } + + /** + * Minimum of two (positive) numbers. Zero is treated as +infinity. + */ + rational min_z(rational const& a, rational const& b) { + if (a.is_zero()) return b; + if (b.is_zero()) return a; + if (a < b) return a; + return b; + } + + rational min_of_delta(uint_set const& subC) { + rational delta(0); + for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { + unsigned j = *it; + wcore const& core = m_cores[j]; + rational new_delta = rational(1) + core.m_upper - core.m_mid; + SASSERT(new_delta.is_pos()); + delta = min_z(delta, new_delta); + } + return delta; + } + + rational sum_of_lower(uint_set const& subC) { + rational lower(0); + for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { + lower += m_cores[*it].m_lower; + } + return lower; + } + + rational sum_of_sigmas(unsigned_vector const& R) { + rational sum(0); + for (unsigned i = 0; i < R.size(); ++i) { + sum += m_sigmas[R[i]]; + } + return sum; + } + void union_Rs(uint_set const& subC, unsigned_vector& R) { + for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { + R.append(m_cores[*it].m_R); + } + } + rational compute_lower() { + rational result(0); + for (unsigned i = 0; i < m_cores.size(); ++i) { + result += m_cores[i].m_lower; + } + return result; + } + void subtract(vector& cores, uint_set const& subC) { + unsigned j = 0; + for (unsigned i = 0; i < cores.size(); ++i) { + if (subC.contains(i)) { + m_asm_set.remove(cores[i].m_r); + } + else { + if (j != i) { + cores[j] = cores[i]; + } + ++j; + } + } + cores.resize(j); + for (unsigned i = 0; i < cores.size(); ++i) { + m_relax2index.insert(cores[i].m_r, i); + } + } + void core2indices(ptr_vector const& core, uint_set& subC, uint_set& soft) { + for (unsigned i = 0; i < core.size(); ++i) { + unsigned j; + expr* a; + VERIFY(m.is_not(core[i], a)); + if (m_relax2index.find(a, j)) { + subC.insert(j); + } + else { + VERIFY(m_soft2index.find(a, j)); + soft.insert(j); + } + } + } + rational refine(unsigned_vector const& idx, rational v) { + return v + rational(1); + } + void relax(uint_set& subC, uint_set& soft, unsigned_vector& R, rational& delta) { + for (uint_set::iterator it = soft.begin(); it != soft.end(); ++it) { + R.push_back(*it); + delta = min_z(delta, m_weights[*it]); + m_asm_set.remove(m_soft_aux[*it].get()); + } + } + void assert_cores() { + for (unsigned i = 0; i < m_cores.size(); ++i) { + assert_core(m_cores[i]); + } + } + void assert_core(wcore const& core) { + expr_ref fml(m); + vector ws; + ptr_vector rs; + rational w(0); + for (unsigned j = 0; j < core.m_R.size(); ++j) { + unsigned idx = core.m_R[j]; + ws.push_back(m_weights[idx]); + w += ws.back(); + rs.push_back(m_soft_aux[idx].get()); + } + w.neg(); + w += core.m_mid; + ws.push_back(w); + rs.push_back(core.m_r); + fml = pb.mk_le(ws.size(), ws.c_ptr(), rs.c_ptr(), core.m_mid); + s().assert_expr(fml); + } + void display(std::ostream& out) { + out << "[" << m_lower << ":" << m_upper << "]\n"; + s().display(out); + out << "\n"; + for (unsigned i = 0; i < m_cores.size(); ++i) { + wcore const& c = m_cores[i]; + out << mk_pp(c.m_r, m) << ": "; + for (unsigned j = 0; j < c.m_R.size(); ++j) { + out << c.m_R[j] << " (" << m_sigmas[c.m_R[j]] << ") "; + } + out << "[" << c.m_lower << ":" << c.m_mid << ":" << c.m_upper << "]\n"; + } + for (unsigned i = 0; i < m_soft.size(); ++i) { + out << mk_pp(m_soft[i], m) << " " << m_weights[i] << "\n"; + } + } + }; + + maxsmt_solver_base* mk_bcd2( + maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { + return alloc(bcd2, c, ws, soft); + } + +} diff --git a/src/opt/bcd2.h b/src/opt/bcd2.h new file mode 100644 index 000000000..50b7215e3 --- /dev/null +++ b/src/opt/bcd2.h @@ -0,0 +1,28 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + bcd2.h + +Abstract: + + Bcd2 based MaxSAT. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-17 + +Notes: + +--*/ + +#ifndef BCD2_H_ +#define BCD2_H_ + +#include "maxsmt.h" + +namespace opt { + maxsmt_solver_base* mk_bcd2(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); +} +#endif diff --git a/src/opt/fu_malik.cpp b/src/opt/fu_malik.cpp new file mode 100644 index 000000000..7a3f685e6 --- /dev/null +++ b/src/opt/fu_malik.cpp @@ -0,0 +1,237 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + fu_malik.cpp + +Abstract: + Fu & Malik built-in optimization method. + Adapted from sample code in C. + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-15 + +Notes: + +--*/ + +#include "fu_malik.h" +#include "qfbv_tactic.h" +#include "tactic2solver.h" +#include "goal.h" +#include "probe.h" +#include "tactic.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" +#include "opt_context.h" + +/** + \brief Fu & Malik procedure for MaxSAT. This procedure is based on + unsat core extraction and the at-most-one constraint. + + Return the number of soft-constraints that can be + satisfied. Return -1 if the hard-constraints cannot be + satisfied. That is, the formula cannot be satisfied even if all + soft-constraints are ignored. + + For more information on the Fu & Malik procedure: + + Z. Fu and S. Malik, On solving the partial MAX-SAT problem, in International + Conference on Theory and Applications of Satisfiability Testing, 2006. +*/ +namespace opt { + + class fu_malik : public maxsmt_solver_base { + filter_model_converter& m_fm; + expr_ref_vector m_aux_soft; + expr_ref_vector m_aux; + model_ref m_model; + + public: + fu_malik(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): + maxsmt_solver_base(c, ws, soft), + m_fm(c.fm()), + m_aux_soft(soft), + m_aux(m) + { + m_upper = rational(m_aux_soft.size() + 1); + m_lower.reset(); + m_assignment.resize(m_aux_soft.size(), false); + } + + /** + \brief One step of the Fu&Malik algorithm. + + Input: soft constraints + aux-vars (aka answer literals) + Output: done/not-done when not done return updated set of soft-constraints and aux-vars. + - if SAT --> terminates + - if UNSAT + * compute unsat core + * add blocking variable to soft-constraints in the core + - replace soft-constraint with the one with the blocking variable + - we should also add an aux-var + - replace aux-var with a new one + * add at-most-one constraint with blocking + */ + + typedef obj_hashtable expr_set; + + void set2vector(expr_set const& set, expr_ref_vector & es) const { + es.reset(); + expr_set::iterator it = set.begin(), end = set.end(); + for (; it != end; ++it) { + es.push_back(*it); + } + } + + void collect_statistics(statistics& st) const { + st.update("opt-fm-num-steps", m_aux_soft.size() + 2 - m_upper.get_unsigned()); + } + + void set_union(expr_set const& set1, expr_set const& set2, expr_set & set) const { + set.reset(); + expr_set::iterator it = set1.begin(), end = set1.end(); + for (; it != end; ++it) { + set.insert(*it); + } + it = set2.begin(); + end = set2.end(); + for (; it != end; ++it) { + set.insert(*it); + } + } + + lbool step() { + IF_VERBOSE(1, verbose_stream() << "(opt.max_sat step " << m_aux_soft.size() + 2 - m_upper.get_unsigned() << ")\n";); + expr_ref_vector assumptions(m), block_vars(m); + for (unsigned i = 0; i < m_aux_soft.size(); ++i) { + assumptions.push_back(m.mk_not(m_aux[i].get())); + } + lbool is_sat = s().check_sat(assumptions.size(), assumptions.c_ptr()); + if (is_sat != l_false) { + return is_sat; + } + + ptr_vector core; + s().get_unsat_core(core); + + SASSERT(!core.empty()); + + // Update soft-constraints and aux_vars + for (unsigned i = 0; i < m_aux_soft.size(); ++i) { + + bool found = false; + for (unsigned j = 0; !found && j < core.size(); ++j) { + found = assumptions[i].get() == core[j]; + } + if (!found) { + continue; + } + app_ref block_var(m), tmp(m); + block_var = m.mk_fresh_const("block_var", m.mk_bool_sort()); + m_aux[i] = m.mk_fresh_const("aux", m.mk_bool_sort()); + m_fm.insert(block_var->get_decl()); + m_fm.insert(to_app(m_aux[i].get())->get_decl()); + m_aux_soft[i] = m.mk_or(m_aux_soft[i].get(), block_var); + block_vars.push_back(block_var); + tmp = m.mk_or(m_aux_soft[i].get(), m_aux[i].get()); + s().assert_expr(tmp); + } + SASSERT (!block_vars.empty()); + assert_at_most_one(block_vars); + IF_VERBOSE(1, verbose_stream() << "(opt.max_sat # of non-blocked soft constraints: " << m_aux_soft.size() - block_vars.size() << ")\n";); + return l_false; + } + + void assert_at_most_one(expr_ref_vector const& block_vars) { + expr_ref has_one(m), has_zero(m), at_most_one(m); + mk_at_most_one(block_vars.size(), block_vars.c_ptr(), has_one, has_zero); + at_most_one = m.mk_or(has_one, has_zero); + s().assert_expr(at_most_one); + } + + void mk_at_most_one(unsigned n, expr* const * vars, expr_ref& has_one, expr_ref& has_zero) { + SASSERT(n != 0); + if (n == 1) { + has_one = vars[0]; + has_zero = m.mk_not(vars[0]); + } + else { + unsigned mid = n/2; + expr_ref has_one1(m), has_one2(m), has_zero1(m), has_zero2(m); + mk_at_most_one(mid, vars, has_one1, has_zero1); + mk_at_most_one(n-mid, vars+mid, has_one2, has_zero2); + has_one = m.mk_or(m.mk_and(has_one1, has_zero2), m.mk_and(has_one2, has_zero1)); + has_zero = m.mk_and(has_zero1, has_zero2); + } + } + + + // TBD: bug when cancel flag is set, fu_malik returns is_sat == l_true instead of l_undef + virtual lbool operator()() { + lbool is_sat = l_true; + if (m_aux_soft.empty()) { + return is_sat; + } + solver::scoped_push _sp(s()); + expr_ref tmp(m); + + TRACE("opt", + tout << "soft constraints:\n"; + for (unsigned i = 0; i < m_aux_soft.size(); ++i) { + tout << mk_pp(m_aux_soft[i].get(), m) << "\n"; + }); + + for (unsigned i = 0; i < m_aux_soft.size(); ++i) { + m_aux.push_back(m.mk_fresh_const("p", m.mk_bool_sort())); + m_fm.insert(to_app(m_aux.back())->get_decl()); + tmp = m.mk_or(m_aux_soft[i].get(), m_aux[i].get()); + s().assert_expr(tmp); + } + + do { + is_sat = step(); + --m_upper; + } + while (is_sat == l_false); + + if (is_sat == l_true) { + // Get a list satisfying m_aux_soft + s().get_model(m_model); + m_lower = m_upper; + m_assignment.reset(); + for (unsigned i = 0; i < m_soft.size(); ++i) { + expr_ref val(m); + VERIFY(m_model->eval(m_soft[i], val)); + TRACE("opt", tout << val << "\n";); + m_assignment.push_back(m.is_true(val)); + } + TRACE("opt", tout << "maxsat cost: " << m_upper << "\n"; + model_smt2_pp(tout, m, *m_model, 0);); + } + // We are done and soft_constraints has + // been updated with the max-sat assignment. + return is_sat; + } + + virtual void get_model(model_ref& mdl) { + mdl = m_model.get(); + } + + virtual rational get_lower() const { + return rational(m_aux_soft.size())-m_upper; + } + + virtual rational get_upper() const { + return rational(m_aux_soft.size())-m_lower; + } + }; + + maxsmt_solver_base* mk_fu_malik(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft) { + return alloc(fu_malik, c, ws, soft); + } + +}; + diff --git a/src/opt/fu_malik.h b/src/opt/fu_malik.h new file mode 100644 index 000000000..52e960b5e --- /dev/null +++ b/src/opt/fu_malik.h @@ -0,0 +1,37 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + fu_malik.h + +Abstract: + + Fu&Malik built-in optimization method. + Adapted from sample code in C. + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-15 + +Notes: + + Takes solver with hard constraints added. + Returns a maximal satisfying subset of soft_constraints + that are still consistent with the solver state. + +--*/ +#ifndef OPT_FU_MALIK_H_ +#define OPT_FU_MALIK_H_ + +#include "opt_solver.h" +#include "maxsmt.h" + +namespace opt { + + maxsmt_solver_base* mk_fu_malik(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); + + +}; + +#endif diff --git a/src/opt/hitting_sets.cpp b/src/opt/hitting_sets.cpp new file mode 100644 index 000000000..54ecaf35b --- /dev/null +++ b/src/opt/hitting_sets.cpp @@ -0,0 +1,1092 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + hitting_sets.h + +Abstract: + + Hitting set approximations. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-06-06 + +Notes: + +--*/ +#include "vector.h" +#include "util.h" +#include "hitting_sets.h" +#include "simplex.h" +#include "sparse_matrix_def.h" +#include "simplex_def.h" + +typedef simplex::simplex Simplex; +typedef simplex::sparse_matrix sparse_matrix; + + +namespace opt { + + struct hitting_sets::imp { + class justification { + public: + enum kind_t { AXIOM, DECISION, CLAUSE }; + private: + kind_t m_kind; + unsigned m_value; + bool m_pos; + public: + explicit justification(kind_t k):m_kind(k), m_value(0), m_pos(false) {} + explicit justification(unsigned v, bool pos):m_kind(CLAUSE), m_value(v), m_pos(pos) {} + justification(justification const& other): + m_kind(other.m_kind), m_value(other.m_value), m_pos(other.m_pos) {} + justification& operator=(justification const& other) { + m_kind = other.m_kind; + m_value = other.m_value; + m_pos = other.m_pos; + return *this; + } + unsigned clause() const { return m_value; } + bool is_axiom() const { return m_kind == AXIOM; } + bool is_decision() const { return m_kind == DECISION; } + bool is_clause() const { return m_kind == CLAUSE; } + kind_t kind() const { return m_kind; } + bool pos() const { return m_pos; } + }; + + class set { + unsigned m_num_elems; + unsigned m_elems[0]; + set(): m_num_elems(0) {} + public: + + static set* mk(small_object_allocator& alloc, unsigned sz, unsigned const* elems) { + unsigned size = (sz+1)*sizeof(unsigned); + void * mem = alloc.allocate(size); + set* result = new (mem) set(); + result->m_num_elems = sz; + memcpy(result->m_elems, elems, sizeof(unsigned)*sz); + return result; + } + + inline unsigned operator[](unsigned idx) const { + SASSERT(idx < m_num_elems); + return m_elems[idx]; + } + + inline unsigned& operator[](unsigned idx) { + SASSERT(idx < m_num_elems); + return m_elems[idx]; + } + + unsigned size() const { return m_num_elems; } + + unsigned alloc_size() const { return (m_num_elems + 1)*sizeof(unsigned); } + + bool empty() const { return 0 == size(); } + }; + + volatile bool m_cancel; + rational m_lower; + rational m_upper; + vector m_weights; + vector m_weights_inv; + rational m_max_weight; + rational m_denominator; + small_object_allocator m_alloc; + ptr_vector m_T; + ptr_vector m_F; + svector m_value; + svector m_model; + vector m_tuse_list; + vector m_fuse_list; + + // Custom CDCL solver. + svector m_justification; + vector m_twatch; + vector m_fwatch; + unsigned_vector m_level; + unsigned_vector m_trail; // trail of assigned literals + unsigned m_qhead; // queue head + justification m_conflict_j; // conflict justification + unsigned m_conflict_l; // conflict literal + bool m_inconsistent; + unsigned m_scope_lvl; + rational m_weight; // current weight of assignment. + unsigned_vector m_indices; + unsigned_vector m_scores; + vector m_scored_weights; + svector m_score_updated; + bool m_enable_simplex; + struct compare_scores { + imp* m_imp; + compare_scores():m_imp(0) {} + bool operator()(int v1, int v2) const { + return m_imp->m_scored_weights[v1] > m_imp->m_scored_weights[v2]; + } + }; + compare_scores m_compare_scores; + heap m_heap; + svector m_mark; + struct scope { + unsigned m_trail_lim; + }; + vector m_scopes; + unsigned_vector m_lemma; + unsigned m_conflict_lvl; + + // simplex + unsynch_mpz_manager m; + Simplex m_simplex; + unsigned m_weights_var; + + static unsigned const null_idx = UINT_MAX; + + imp(): + m_cancel(false), + m_max_weight(0), + m_denominator(1), + m_alloc("hitting-sets"), + m_qhead(0), + m_conflict_j(justification(justification::AXIOM)), + m_inconsistent(false), + m_scope_lvl(0), + m_compare_scores(), + m_heap(0, m_compare_scores), + m_weights_var(0) { + m_enable_simplex = true; + m_compare_scores.m_imp = this; + } + ~imp() { + for (unsigned i = 0; i < m_T.size(); ++i) { + m_alloc.deallocate(m_T[i]->alloc_size(), m_T[i]); + } + for (unsigned i = 0; i < m_F.size(); ++i) { + m_alloc.deallocate(m_F[i]->alloc_size(), m_F[i]); + } + } + + void add_weight(rational const& w) { + SASSERT(w.is_pos()); + unsigned var = m_weights.size(); + m_simplex.ensure_var(var); + m_simplex.set_lower(var, mpq_inf(mpq(0),mpq(0))); + m_simplex.set_upper(var, mpq_inf(mpq(1),mpq(0))); + m_weights.push_back(w); + m_weights_inv.push_back(rational::one()); + m_value.push_back(l_undef); + m_justification.push_back(justification(justification::DECISION)); + m_tuse_list.push_back(unsigned_vector()); + m_fuse_list.push_back(unsigned_vector()); + m_twatch.push_back(unsigned_vector()); + m_fwatch.push_back(unsigned_vector()); + m_level.push_back(0); + m_indices.push_back(var); + m_model.push_back(l_undef); + m_mark.push_back(false); + m_scores.push_back(0); + m_scored_weights.push_back(rational(0)); + m_score_updated.push_back(true); + m_max_weight += w; + } + + justification add_exists_false(unsigned sz, unsigned const* S) { + return add_exists(sz, S, true); + } + + justification add_exists_true(unsigned sz, unsigned const* S) { + return add_exists(sz, S, false); + } + + justification add_exists(unsigned sz, unsigned const* S, bool sign) { + vector& use_list = sign?m_fuse_list:m_tuse_list; + lbool val = sign?l_false:l_true; + justification j(justification::AXIOM); + ptr_vector& Sets = sign?m_F:m_T; + vector& watch = sign?m_fwatch:m_twatch; + init_weights(); + if (sz == 0) { + set_conflict(0, justification(justification::AXIOM)); + } + else if (sz == 1) { + IF_VERBOSE(2, verbose_stream() << "unit literal : " << S[0] << " " << val << "\n";); + assign(S[0], val, justification(justification::AXIOM)); + } + else { + unsigned clause_id = Sets.size(); + for (unsigned i = 0; i < sz; ++i) { + use_list[S[i]].push_back(clause_id); + } + j = justification(clause_id, !sign); + watch[S[0]].push_back(clause_id); + watch[S[1]].push_back(clause_id); + Sets.push_back(set::mk(m_alloc, sz, S)); + if (!sign) { + pop(scope_lvl()); + inc_score(clause_id); + } + TRACE("opt", display(tout, j);); + IF_VERBOSE(2, if (!sign) display(verbose_stream(), j);); + if (!sign && m_enable_simplex) { + add_simplex_row(!sign, sz, S); + } + } + return j; + } + + lbool compute_lower() { + m_lower.reset(); + rational w1 = L1(); + rational w2 = L2(); + rational w3 = L3(); + if (w1 > m_lower) m_lower = w1; + if (w2 > m_lower) m_lower = w2; + if (w3 > m_lower) m_lower = w3; + return l_true; + } + + lbool compute_upper() { + m_upper = m_max_weight; + unsigned fsz = m_F.size(); + lbool r = search(); + pop(scope_lvl()); + + + IF_VERBOSE(1, verbose_stream() << "(hsmax.negated-size: " << fsz << ")\n";); +#if 0 + // garbage collect agressively on exit. + // all learned clases for negative branches are + // pruned. + for (unsigned i = fsz; i < m_F.size(); ++i) { + m_alloc.deallocate(m_F[i]->alloc_size(), m_F[i]); + } + m_F.resize(fsz); + for (unsigned i = 0; i < m_fuse_list.size(); ++i) { + unsigned_vector & uses = m_fuse_list[i]; + while (!uses.empty() && uses.back() >= fsz) uses.pop_back(); + unsigned_vector & watch = m_fwatch[i]; + unsigned j = 0, k = 0; + for (; j < watch.size(); ++j) { + if (watch[j] < fsz) { + watch[k] = watch[j]; + ++k; + } + } + watch.resize(k); + } +#endif + return r; + } + + rational get_lower() { + return m_lower/m_denominator; + } + + rational get_upper() { + return m_upper/m_denominator; + } + + void set_upper(rational const& r) { + m_max_weight = r*m_denominator; + } + + bool get_value(unsigned idx) { + return + idx < m_model.size() && + m_model[idx] == l_true; + } + + void set_cancel(bool f) { + m_cancel = f; + m_simplex.set_cancel(f); + } + + void collect_statistics(::statistics& st) const { + m_simplex.collect_statistics(st); + } + + void reset() { + m_lower.reset(); + m_upper = m_max_weight; + } + + void init_weights() { + if (m_weights_var != 0) { + return; + } + m_weights_var = m_weights.size(); + unsigned_vector vars; + scoped_mpz_vector coeffs(m); + + // normalize weights to integral. + rational d(1); + for (unsigned i = 0; i < m_weights.size(); ++i) { + d = lcm(d, denominator(m_weights[i])); + } + m_denominator = d; + if (!d.is_one()) { + for (unsigned i = 0; i < m_weights.size(); ++i) { + m_weights[i] *= d; + } + } + rational lc(1); + for (unsigned i = 0; i < m_weights.size(); ++i) { + lc = lcm(lc, m_weights[i]); + } + for (unsigned i = 0; i < m_weights.size(); ++i) { + m_weights_inv[i] = lc/m_weights[i]; + } + + m_heap.set_bounds(m_weights.size()); + for (unsigned i = 0; i < m_weights.size(); ++i) { + m_heap.insert(i); + } + update_heap(); + + // set up Simplex objective function. + for (unsigned i = 0; i < m_weights.size(); ++i) { + vars.push_back(i); + coeffs.push_back(m_weights[i].to_mpq().numerator()); + } + m_simplex.ensure_var(m_weights_var); + vars.push_back(m_weights_var); + coeffs.push_back(mpz(-1)); + m_simplex.add_row(m_weights_var, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); + + } + + void display(std::ostream& out) const { + out << "inconsistent: " << m_inconsistent << "\n"; + out << "weight: " << m_weight << "\n"; + for (unsigned i = 0; i < m_weights.size(); ++i) { + out << i << ": " << value(i) << " w: " << m_weights[i] << " s: " << m_scores[i] << "\n"; + } + for (unsigned i = 0; i < m_T.size(); ++i) { + display(out << "+" << i << ": ", *m_T[i]); + } + for (unsigned i = 0; i < m_F.size(); ++i) { + display(out << "-" << i << ": ", *m_F[i]); + } + out << "watch lists:\n"; + for (unsigned i = 0; i < m_fwatch.size(); ++i) { + out << i << ": "; + for (unsigned j = 0; j < m_twatch[i].size(); ++j) { + out << "+" << m_twatch[i][j] << " "; + } + for (unsigned j = 0; j < m_fwatch[i].size(); ++j) { + out << "-" << m_fwatch[i][j] << " "; + } + out << "\n"; + } + out << "trail\n"; + for (unsigned i = 0; i < m_trail.size(); ++i) { + unsigned idx = m_trail[i]; + out << (m_justification[idx].is_decision()?"d":"") << idx << " "; + } + out << "\n"; + } + + void display(std::ostream& out, set const& S) const { + for (unsigned i = 0; i < S.size(); ++i) { + out << S[i] << " "; + } + out << "\n"; + } + + void display(std::ostream& out, justification const& j) const { + switch(j.kind()) { + case justification::AXIOM: + out << "axiom\n"; + break; + case justification::DECISION: + out << "decision\n"; + break; + case justification::CLAUSE: { + out << "clause: "; + set const& S = j.pos()?(*m_T[j.clause()]):(*m_F[j.clause()]); + for (unsigned i = 0; i < S.size(); ++i) { + out << S[i] << " "; + } + out << "\n"; + } + } + } + + void display_lemma(std::ostream& out) { + out << "lemma: "; + for (unsigned i = 0; i < m_lemma.size(); ++i) { + out << m_lemma[i] << " "; + } + out << "\n"; + } + + struct scoped_push { + imp& s; + scoped_push(imp& s):s(s) { s.push(); } + ~scoped_push() { s.pop(1); } + }; + + struct value_lt { + vector const& weights; + value_lt(vector const& weights): + weights(weights) {} + bool operator()(int v1, int v2) const { + return weights[v1] > weights[v2]; + } + }; + + void inc_score(unsigned clause_id) { + set const& S = *m_T[clause_id]; + if (!has_selected(S)) { + for (unsigned j = 0; j < S.size(); ++j) { + ++m_scores[S[j]]; + m_score_updated[S[j]] = true; + } + } + } + + void dec_score(unsigned clause_id) { + set const& S = *m_T[clause_id]; + if (!has_selected(S)) { + for (unsigned j = 0; j < S.size(); ++j) { + SASSERT(m_scores[S[j]] > 0); + --m_scores[S[j]]; + m_score_updated[S[j]] = true; + } + } + } + + void update_score(unsigned idx, bool inc) { + unsigned_vector const& uses = m_tuse_list[idx]; + for (unsigned i = 0; i < uses.size(); ++i) { + if (inc) { + inc_score(uses[i]); + } + else { + dec_score(uses[i]); + } + } + } + + rational L1() { + rational w(m_weight); + scoped_push _sc(*this); + for (unsigned i = 0; !canceled() && i < m_T.size(); ++i) { + set const& S = *m_T[i]; + SASSERT(!S.empty()); + if (!has_selected(S)) { + w += m_weights[select_min(S)]; + for (unsigned j = 0; j < S.size(); ++j) { + assign(S[j], l_true, justification(justification::DECISION)); + } + } + } + return w; + } + + void update_heap() { + for (unsigned i = 0; i < m_scored_weights.size(); ++i) { + if (m_score_updated[i]) { + rational const& old_w = m_scored_weights[i]; + rational new_w = rational(m_scores[i])*m_weights_inv[i]; + if (new_w > old_w) { + m_scored_weights[i] = new_w; + //m_heap.decreased(i); + } + else if (new_w < old_w) { + m_scored_weights[i] = new_w; + //m_heap.increased(i); + } + m_score_updated[i] = false; + } + } + } + + rational L2() { + rational w(m_weight); + scoped_push _sc(*this); + int n = 0; + for (unsigned i = 0; i < m_T.size(); ++i) { + if (!has_selected(*m_T[i])) ++n; + } + + update_heap(); + value_lt lt(m_scored_weights); + std::sort(m_indices.begin(), m_indices.end(), lt); + for(unsigned i = 0; i < m_indices.size() && n > 0; ++i) { + // deg(c) = score(c) + // wt(c) = m_weights[c] + + unsigned idx = m_indices[i]; + if (m_scores[idx] == 0) { + break; + } + if (m_scores[idx] < static_cast(n) || m_weights[idx].is_one()) { + w += m_weights[idx]; + } + else { + w += div((rational(n)*m_weights[idx]), rational(m_scores[idx])); + } + n -= m_scores[idx]; + } + return w; + } + + rational L3() { + TRACE("simplex", m_simplex.display(tout);); + VERIFY(l_true == m_simplex.make_feasible()); + TRACE("simplex", m_simplex.display(tout);); + VERIFY(l_true == m_simplex.minimize(m_weights_var)); + mpq_inf const& val = m_simplex.get_value(m_weights_var); + unsynch_mpq_inf_manager mg; + unsynch_mpq_manager& mq = mg.get_mpq_manager(); + scoped_mpq c(mq); + mg.ceil(val, c); + rational w(c); + CTRACE("simplex", + w >= m_weight, tout << w << " " << m_weight << " !!!!\n"; + display(tout);); + SASSERT(w >= m_weight); + return w; + } + + void add_simplex_row(bool is_some_true, unsigned sz, unsigned const* S) { + unsigned_vector vars; + scoped_mpz_vector coeffs(m); + for (unsigned i = 0; i < sz; ++i) { + vars.push_back(S[i]); + coeffs.push_back(mpz(1)); + } + unsigned base_var = m_F.size() + m_T.size() + m_weights.size(); + m_simplex.ensure_var(base_var); + vars.push_back(base_var); + coeffs.push_back(mpz(-1)); + // S - base_var = 0 + if (is_some_true) { + // base_var >= 1 + m_simplex.set_lower(base_var, mpq_inf(mpq(1),mpq(0))); + } + else { + // base_var <= sz-1 + m_simplex.set_upper(base_var, mpq_inf(mpq(sz-1),mpq(0))); + } + m_simplex.add_row(base_var, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); + } + + unsigned select_min(set const& S) { + unsigned result = S[0]; + for (unsigned i = 1; i < S.size(); ++i) { + if (m_weights[result] > m_weights[S[i]]) { + result = S[i]; + } + } + return result; + } + + bool have_selected(lbool val, ptr_vector const& Sets, unsigned& i) { + for (i = 0; i < Sets.size(); ++i) { + if (!has_selected(val, *Sets[i])) return false; + } + return true; + } + + void set_undef_to_false() { + for (unsigned i = 0; i < m_model.size(); ++i) { + if (m_model[i] == l_undef) { + m_model[i] = l_false; + } + } + } + + bool values_satisfy_Fs(unsigned& i) { + unsigned j = 0; + for (i = 0; i < m_F.size(); ++i) { + set const& F = *m_F[i]; + for (j = 0; j < F.size(); ++j) { + if (m_model[F[j]] == l_false) { + break; + } + } + if (F.size() == j) { + break; + } + } + return i == m_F.size(); + } + + bool has_selected(set const& S) { + return has_selected(l_true, S); + } + + bool has_unselected(set const& S) { + return has_selected(l_false, S); + } + + bool has_unset(set const& S) { + return has_selected(l_undef, S); + } + + bool has_selected(lbool val, set const& S) { + for (unsigned i = 0; i < S.size(); ++i) { + if (val == value(S[i])) { + return true; + } + } + return false; + } + + // (greedy) CDCL learner for hitting sets. + + inline unsigned scope_lvl() const { return m_scope_lvl; } + inline bool inconsistent() const { return m_inconsistent; } + inline bool canceled() const { return m_cancel; } + inline unsigned lvl(unsigned idx) const { return m_level[idx]; } + inline lbool value(unsigned idx) const { return m_value[idx]; } + + inline bool is_marked(unsigned v) const { return m_mark[v] != 0; } + inline void mark(unsigned v) { SASSERT(!is_marked(v)); m_mark[v] = true; } + inline void reset_mark(unsigned v) { SASSERT(is_marked(v)); m_mark[v] = false; } + + void push() { + SASSERT(!inconsistent()); + ++m_scope_lvl; + m_scopes.push_back(scope()); + scope& s = m_scopes.back(); + s.m_trail_lim = m_trail.size(); + } + + void pop(unsigned n) { + if (n > 0) { + m_inconsistent = false; + m_scope_lvl = scope_lvl() - n; + unassign(m_scopes[scope_lvl()].m_trail_lim); + m_scopes.shrink(scope_lvl()); + } + } + + void assign(unsigned idx, lbool val, justification const& justification) { + if (val == l_true) { + m_weight += m_weights[idx]; + update_score(idx, false); + if (m_enable_simplex) { + m_simplex.set_lower(idx, mpq_inf(mpq(1),mpq(0))); + } + } + SASSERT(val != l_true || m_scores[idx] == 0); + m_value[idx] = val; + m_justification[idx] = justification; + m_trail.push_back(idx); + m_level[idx] = scope_lvl(); + TRACE("opt", tout << idx << " := " << val << " scope: " << scope_lvl() << " w: " << m_weight << "\n";); + } + + + svector m_replay_idx; + svector m_replay_val; + void unassign(unsigned sz) { + for (unsigned j = sz; j < m_trail.size(); ++j) { + unsigned idx = m_trail[j]; + lbool val = value(idx); + m_value[idx] = l_undef; + if (val == l_true) { + m_weight -= m_weights[idx]; + update_score(idx, true); + if (m_enable_simplex) { + m_simplex.set_lower(idx, mpq_inf(mpq(0),mpq(0))); + } + } + if (m_justification[idx].is_axiom()) { + m_replay_idx.push_back(idx); + m_replay_val.push_back(val); + } + } + TRACE("opt", tout << m_weight << "\n";); + m_trail.shrink(sz); + m_qhead = sz; + for (unsigned i = m_replay_idx.size(); i > 0; ) { + --i; + unsigned idx = m_replay_idx[i]; + lbool val = m_replay_val[i]; + assign(idx, val, justification(justification::AXIOM)); + } + m_replay_idx.reset(); + m_replay_val.reset(); + } + + + lbool search() { + TRACE("opt", display(tout);); + pop(scope_lvl()); + while (true) { + while (true) { + propagate(); + if (canceled()) return l_undef; + if (!inconsistent()) break; + if (!resolve_conflict()) return l_false; + SASSERT(!inconsistent()); + } + if (!decide()) { + SASSERT(validate_model()); + m_model.reset(); + m_model.append(m_value); + m_upper = m_weight; + // SASSERT(m_weight < m_max_weight); + return l_true; + } + } + } + + bool validate_model() { + for (unsigned i = 0; i < m_T.size(); ++i) { + set const& S = *m_T[i]; + bool found = false; + for (unsigned j = 0; !found && j < S.size(); ++j) { + found = value(S[j]) == l_true; + } + CTRACE("opt", !found, + display(tout << "not found: " << i << "\n", S); + display(tout);); + SASSERT(found); + } + for (unsigned i = 0; i < m_F.size(); ++i) { + set const& S = *m_F[i]; + bool found = false; + for (unsigned j = 0; !found && j < S.size(); ++j) { + found = value(S[j]) != l_true; + } + CTRACE("opt", !found, + display(tout << "not found: " << i << "\n", S); + display(tout);); + SASSERT(found); + } + + return true; + } + + bool invariant() { + for (unsigned i = 0; i < m_fwatch.size(); ++i) { + for (unsigned j = 0; j < m_fwatch[i].size(); ++j) { + set const& S = *m_F[m_fwatch[i][j]]; + SASSERT(S[0] == i || S[1] == i); + } + } + for (unsigned i = 0; i < m_twatch.size(); ++i) { + for (unsigned j = 0; j < m_twatch[i].size(); ++j) { + set const& S = *m_T[m_twatch[i][j]]; + SASSERT(S[0] == i || S[1] == i); + } + } + return true; + } + + bool resolve_conflict() { + while (true) { + if (!resolve_conflict_core()) return false; + if (!inconsistent()) return true; + } + } + + unsigned get_max_lvl(unsigned conflict_l, justification const& conflict_j) { + if (scope_lvl() == 0) return 0; + unsigned r = lvl(conflict_l); + if (conflict_j.is_clause()) { + unsigned clause = conflict_j.clause(); + ptr_vector const& S = conflict_j.pos()?m_T:m_F; + r = std::max(r, lvl((*S[clause])[0])); + r = std::max(r, lvl((*S[clause])[1])); + } + return r; + } + + bool resolve_conflict_core() { + SASSERT(inconsistent()); + TRACE("opt", display(tout);); + unsigned conflict_l = m_conflict_l; + justification conflict_j(m_conflict_j); + if (conflict_j.is_axiom()) { + return false; + } + m_conflict_lvl = get_max_lvl(conflict_l, conflict_j); + if (m_conflict_lvl == 0) { + return false; + } + unsigned idx = skip_above_conflict_level(); + unsigned num_marks = 0; + m_lemma.reset(); + m_lemma.push_back(0); + process_antecedent(conflict_l, num_marks); + do { + TRACE("opt", tout << "conflict literal: " << conflict_l << "\n"; + display(tout, conflict_j);); + if (conflict_j.is_clause()) { + unsigned cl = conflict_j.clause(); + unsigned i = 0; + SASSERT(value(conflict_l) != l_undef); + set const& T = conflict_j.pos()?(*m_T[cl]):(*m_F[cl]); + if (T[0] == conflict_l) { + i = 1; + } + else { + SASSERT(T[1] == conflict_l); + process_antecedent(T[0], num_marks); + i = 2; + } + unsigned sz = T.size(); + for (; i < sz; ++i) { + process_antecedent(T[i], num_marks); + } + } + else if (conflict_j.is_decision()) { + --num_marks; + SASSERT(num_marks == 0); + break; + } + else if (conflict_j.is_axiom()) { + IF_VERBOSE(0, verbose_stream() << "axiom " << conflict_l << " " << value(conflict_l) << " " << num_marks << "\n";); + --num_marks; + SASSERT(num_marks == 0); + break; + } + while (true) { + unsigned l = m_trail[idx]; + if (is_marked(l)) break; + SASSERT(idx > 0); + --idx; + } + conflict_l = m_trail[idx]; + conflict_j = m_justification[conflict_l]; + --idx; + --num_marks; + if (num_marks == 0 && value(conflict_l) == l_false) { + ++num_marks; + } + reset_mark(conflict_l); + } + while (num_marks > 0); + m_lemma[0] = conflict_l; + TRACE("opt", display_lemma(tout);); + SASSERT(value(conflict_l) == l_true); + unsigned new_scope_lvl = 0; + for (unsigned i = 1; i < m_lemma.size(); ++i) { + SASSERT(l_true == value(m_lemma[i])); + new_scope_lvl = std::max(new_scope_lvl, lvl(m_lemma[i])); + reset_mark(m_lemma[i]); + } + pop(scope_lvl() - new_scope_lvl); + SASSERT(l_undef == value(conflict_l)); + justification j = add_exists_false(m_lemma.size(), m_lemma.c_ptr()); + if (!j.is_axiom()) assign(conflict_l, l_false, j); + return true; + } + + + void process_antecedent(unsigned antecedent, unsigned& num_marks) { + unsigned alvl = lvl(antecedent); + SASSERT(alvl <= m_conflict_lvl); + if (!is_marked(antecedent) && alvl > 0 && !m_justification[antecedent].is_axiom()) { + mark(antecedent); + if (alvl == m_conflict_lvl || value(antecedent) == l_false) { + ++num_marks; + } + else { + m_lemma.push_back(antecedent); + } + } + } + + unsigned skip_above_conflict_level() { + unsigned idx = m_trail.size(); + if (idx == 0) { + return idx; + } + idx--; + // skip literals from levels above the conflict level + while (lvl(m_trail[idx]) > m_conflict_lvl) { + SASSERT(idx > 0); + idx--; + } + return idx; + } + + void set_conflict(unsigned idx, justification const& justification) { + if (!inconsistent()) { + TRACE("opt", tout << "conflict: " << idx << "\n";); + m_inconsistent = true; + m_conflict_j = justification; + m_conflict_l = idx; + } + } + + unsigned next_var() { + update_heap(); + + value_lt lt(m_scored_weights); + std::sort(m_indices.begin(), m_indices.end(), lt); + unsigned idx = m_indices[0]; + if (m_scores[idx] == 0) return UINT_MAX; + return idx; +#if 0 + int min_val = m_heap.min_value(); + if (min_val == -1) { + return UINT_MAX; + } + SASSERT(0 <= min_val && static_cast(min_val) < m_weights.size()); + if (m_scores[min_val] == 0) { + return UINT_MAX; + } + return static_cast(min_val); +#endif + } + + bool decide() { + unsigned idx = next_var(); + if (idx == UINT_MAX) { + return false; + } + else { + push(); + TRACE("opt", tout << "decide " << idx << "\n";); + assign(idx, l_true, justification(justification::DECISION)); + return true; + } + } + + void propagate() { + TRACE("opt", display(tout);); + SASSERT(invariant()); + while (m_qhead < m_trail.size() && !inconsistent() && !canceled()) { + unsigned idx = m_trail[m_qhead]; + ++m_qhead; + switch (value(idx)) { + case l_undef: + UNREACHABLE(); + break; + case l_true: + propagate(idx, l_false, m_fwatch, m_F); + break; + case l_false: + propagate(idx, l_true, m_twatch, m_T); + break; + } + } + prune_branch(); + } + + void propagate(unsigned idx, lbool good_val, vector& watch, ptr_vector& Fs) + { + TRACE("opt", tout << idx << " " << value(idx) << "\n";); + unsigned_vector& w = watch[idx]; + unsigned sz = w.size(); + lbool bad_val = ~good_val; + SASSERT(value(idx) == bad_val); + unsigned l = 0; + for (unsigned i = 0; i < sz && !canceled(); ++i, ++l) { + unsigned clause_id = w[i]; + set& F = *Fs[clause_id]; + SASSERT(F.size() >= 2); + bool k1 = (F[0] != idx); + bool k2 = !k1; + SASSERT(F[k1] == idx); + SASSERT(value(F[k1]) == bad_val); + if (value(F[k2]) == good_val) { + w[l] = w[i]; + continue; + } + bool found = false; + unsigned sz2 = F.size(); + for (unsigned j = 2; !found && j < sz2; ++j) { + unsigned idx2 = F[j]; + if (value(idx2) != bad_val) { + found = true; + std::swap(F[k1], F[j]); + --l; + watch[idx2].push_back(clause_id); + } + } + if (!found) { + if (value(F[k2]) == bad_val) { + set_conflict(F[k2], justification(clause_id, good_val == l_true)); + if (i == l) { + l = sz; + } + else { + for (; i < sz; ++i, ++l) { + w[l] = w[i]; + } + } + break; + } + else { + SASSERT(value(F[k2]) == l_undef); + assign(F[k2], good_val, justification(clause_id, good_val == l_true)); + w[l] = w[i]; + } + } + } + watch[idx].shrink(l); + SASSERT(invariant()); + TRACE("opt", tout << idx << " " << value(idx) << "\n";); + SASSERT(value(idx) == bad_val); + } + + bool infeasible_lookahead() { + if (m_enable_simplex && L3() >= m_max_weight) { + return true; + } + return + (L1() >= m_max_weight) || + (L2() >= m_max_weight); + } + + void prune_branch() { + if (inconsistent() || !infeasible_lookahead()) { + return; + } + + IF_VERBOSE(4, verbose_stream() << "(hs.prune-branch " << m_weight << ")\n";); + m_lemma.reset(); + unsigned i = 0; + rational w(0); + for (; i < m_trail.size() && w < m_max_weight; ++i) { + unsigned idx = m_trail[i]; + if (m_justification[idx].is_decision()) { + SASSERT(value(idx) == l_true); + m_lemma.push_back(idx); + w += m_weights[idx]; + } + } + // undo the lower bounds. + TRACE("opt", + tout << "prune branch: " << m_weight << " "; + display_lemma(tout); + display(tout); + ); + justification j = add_exists_false(m_lemma.size(), m_lemma.c_ptr()); + unsigned idx = m_lemma.empty()?0:m_lemma[0]; + set_conflict(idx, j); + } + + // TBD: derive strong inequalities and add them to Simplex. + // x_i1 + .. + x_ik >= k-1 for each subset k from set n: x_1 + .. + x_n >= k + }; + + + hitting_sets::hitting_sets() { m_imp = alloc(imp); } + hitting_sets::~hitting_sets() { dealloc(m_imp); } + void hitting_sets::add_weight(rational const& w) { m_imp->add_weight(w); } + void hitting_sets::add_exists_true(unsigned sz, unsigned const* elems) { m_imp->add_exists_true(sz, elems); } + void hitting_sets::add_exists_false(unsigned sz, unsigned const* elems) { m_imp->add_exists_false(sz, elems); } + lbool hitting_sets::compute_lower() { return m_imp->compute_lower(); } + lbool hitting_sets::compute_upper() { return m_imp->compute_upper(); } + rational hitting_sets::get_lower() { return m_imp->get_lower(); } + rational hitting_sets::get_upper() { return m_imp->get_upper(); } + void hitting_sets::set_upper(rational const& r) { return m_imp->set_upper(r); } + bool hitting_sets::get_value(unsigned idx) { return m_imp->get_value(idx); } + void hitting_sets::set_cancel(bool f) { m_imp->set_cancel(f); } + void hitting_sets::collect_statistics(::statistics& st) const { m_imp->collect_statistics(st); } + void hitting_sets::reset() { m_imp->reset(); } + + +}; diff --git a/src/opt/hitting_sets.h b/src/opt/hitting_sets.h new file mode 100644 index 000000000..718616a9e --- /dev/null +++ b/src/opt/hitting_sets.h @@ -0,0 +1,51 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + hitting_sets.h + +Abstract: + + Hitting set approximations. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-06-06 + +Notes: + +--*/ +#ifndef HITTING_SETS_H_ +#define HITTING_SETS_H_ + +#include "rational.h" +#include "statistics.h" +#include "lbool.h" + +namespace opt { + + class hitting_sets { + struct imp; + imp* m_imp; + public: + hitting_sets(); + ~hitting_sets(); + void add_weight(rational const& w); + void add_exists_true(unsigned sz, unsigned const* elems); + void add_exists_false(unsigned sz, unsigned const* elems); + lbool compute_lower(); + lbool compute_upper(); + void set_upper(rational const& r); + rational get_lower(); + rational get_upper(); + bool get_value(unsigned idx); + void set_cancel(bool f); + void collect_statistics(::statistics& st) const; + void reset(); + }; + + +}; + +#endif diff --git a/src/opt/maxhs.cpp b/src/opt/maxhs.cpp new file mode 100644 index 000000000..9ce9844b5 --- /dev/null +++ b/src/opt/maxhs.cpp @@ -0,0 +1,561 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + maxhs.cpp + +Abstract: + + maxhs based MaxSAT. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-17 + +Notes: + +--*/ +#include "optsmt.h" +#include "hitting_sets.h" +#include "stopwatch.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" +#include "uint_set.h" +#include "maxhs.h" +#include "opt_context.h" + +namespace opt { + + class scoped_stopwatch { + double& m_time; + stopwatch m_watch; + public: + scoped_stopwatch(double& time): m_time(time) { + m_watch.start(); + } + ~scoped_stopwatch() { + m_watch.stop(); + m_time += m_watch.get_seconds(); + } + }; + + + // ---------------------------------- + // MaxSatHS+MSS + // variant of MaxSAT-HS (Algorithm 9) + // that also refines upper bound during progressive calls + // to the underlying optimization solver for the soft constraints. + // + + class maxhs : public maxsmt_solver_base { + struct stats { + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + unsigned m_num_iterations; + unsigned m_num_core_reductions_success; + unsigned m_num_core_reductions_failure; + unsigned m_num_model_expansions_success; + unsigned m_num_model_expansions_failure; + double m_core_reduction_time; + double m_model_expansion_time; + double m_aux_sat_time; + double m_disjoint_cores_time; + }; + + hitting_sets m_hs; + expr_ref_vector m_aux; // auxiliary (indicator) variables. + obj_map m_aux2index; // expr |-> index + unsigned_vector m_core_activity; // number of times soft constraint is used in a core. + svector m_seed; // clause selected in current model. + svector m_aux_active; // active soft clauses. + ptr_vector m_asms; // assumptions (over aux) + stats m_stats; + bool m_at_lower_bound; + + + public: + maxhs(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): + maxsmt_solver_base(c, ws, soft), + m_aux(m), + m_at_lower_bound(false) { + } + virtual ~maxhs() {} + + virtual void set_cancel(bool f) { + maxsmt_solver_base::set_cancel(f); + m_hs.set_cancel(f); + } + + virtual void collect_statistics(statistics& st) const { + maxsmt_solver_base::collect_statistics(st); + m_hs.collect_statistics(st); + st.update("maxhs-num-iterations", m_stats.m_num_iterations); + st.update("maxhs-num-core-reductions-n", m_stats.m_num_core_reductions_failure); + st.update("maxhs-num-core-reductions-y", m_stats.m_num_core_reductions_success); + st.update("maxhs-num-model-expansions-n", m_stats.m_num_model_expansions_failure); + st.update("maxhs-num-model-expansions-y", m_stats.m_num_model_expansions_success); + st.update("maxhs-core-reduction-time", m_stats.m_core_reduction_time); + st.update("maxhs-model-expansion-time", m_stats.m_model_expansion_time); + st.update("maxhs-aux-sat-time", m_stats.m_aux_sat_time); + st.update("maxhs-disj-core-time", m_stats.m_disjoint_cores_time); + } + + lbool operator()() { + ptr_vector hs; + init(); + init_local(); + if (!disjoint_cores(hs)) { + return l_undef; + } + seed2assumptions(); + while (m_lower < m_upper) { + ++m_stats.m_num_iterations; + trace_bounds("maxhs"); + TRACE("opt", tout << "(maxhs [" << m_lower << ":" << m_upper << "])\n";); + if (m_cancel) { + return l_undef; + } + + lbool core_found = generate_cores(hs); + switch(core_found) { + case l_undef: + return l_undef; + case l_true: { + lbool is_sat = next_seed(); + switch(is_sat) { + case l_true: + seed2hs(false, hs); + break; + case l_false: + TRACE("opt", tout << "no more seeds\n";); + IF_VERBOSE(1, verbose_stream() << "(opt.maxhs.no-more-seeds)\n";); + m_lower = m_upper; + return l_true; + case l_undef: + return l_undef; + } + break; + } + case l_false: + IF_VERBOSE(1, verbose_stream() << "(opt.maxhs.no-more-cores)\n";); + TRACE("opt", tout << "no more cores\n";); + m_lower = m_upper; + return l_true; + } + } + return l_true; + } + + private: + + unsigned num_soft() const { return m_soft.size(); } + + void init_local() { + unsigned sz = num_soft(); + app_ref fml(m), obj(m); + expr_ref_vector sum(m); + m_asms.reset(); + m_seed.reset(); + m_aux.reset(); + m_aux_active.reset(); + m_aux2index.reset(); + m_core_activity.reset(); + for (unsigned i = 0; i < sz; ++i) { + bool tt = is_true(m_model, m_soft[i]); + m_seed.push_back(tt); + m_aux. push_back(mk_fresh(m.mk_bool_sort())); + m_aux_active.push_back(false); + m_core_activity.push_back(0); + m_aux2index.insert(m_aux.back(), i); + if (tt) { + m_asms.push_back(m_aux.back()); + ensure_active(i); + } + } + + for (unsigned i = 0; i < m_weights.size(); ++i) { + m_hs.add_weight(m_weights[i]); + } + TRACE("opt", print_seed(tout);); + } + + + void hs2seed(ptr_vector const& hs) { + for (unsigned i = 0; i < num_soft(); ++i) { + m_seed[i] = true; + } + for (unsigned i = 0; i < hs.size(); ++i) { + m_seed[m_aux2index.find(hs[i])] = false; + } + TRACE("opt", + print_asms(tout << "hitting set: ", hs); + print_seed(tout);); + } + + void seed2hs(bool pos, ptr_vector& hs) { + hs.reset(); + for (unsigned i = 0; i < num_soft(); ++i) { + if (pos == m_seed[i]) { + hs.push_back(m_aux[i].get()); + } + } + TRACE("opt", + print_asms(tout << "hitting set: ", hs); + print_seed(tout);); + + } + + void seed2assumptions() { + seed2hs(true, m_asms); + } + + + // + // Find disjoint cores for soft constraints. + // + bool disjoint_cores(ptr_vector& hs) { + scoped_stopwatch _sw(m_stats.m_disjoint_cores_time); + m_asms.reset(); + svector active(num_soft(), true); + rational lower(0); + update_assumptions(active, lower, hs); + SASSERT(lower.is_zero()); + while (true) { + lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); + switch (is_sat) { + case l_true: + if (lower > m_lower) { + m_lower = lower; + } + return true; + case l_false: + if (!shrink()) return false; + block_up(); + update_assumptions(active, lower, hs); + break; + case l_undef: + return false; + } + } + } + + void update_assumptions(svector& active, rational& lower, ptr_vector& hs) { + rational arg_min(0); + expr* e = 0; + for (unsigned i = 0; i < m_asms.size(); ++i) { + unsigned index = m_aux2index.find(m_asms[i]); + active[index] = false; + if (arg_min.is_zero() || arg_min > m_weights[index]) { + arg_min = m_weights[index]; + e = m_asms[i]; + } + } + if (e) { + hs.push_back(e); + lower += arg_min; + } + m_asms.reset(); + for (unsigned i = 0; i < num_soft(); ++i) { + if (active[i]) { + m_asms.push_back(m_aux[i].get()); + ensure_active(i); + } + } + } + + // + // Auxiliary Algorithm 10 for producing cores. + // + lbool generate_cores(ptr_vector& hs) { + bool core = !m_at_lower_bound; + while (true) { + hs2seed(hs); + lbool is_sat = check_subset(); + switch(is_sat) { + case l_undef: + return l_undef; + case l_true: + if (!grow()) return l_undef; + block_down(); + return core?l_true:l_false; + case l_false: + core = true; + if (!shrink()) return l_undef; + block_up(); + find_non_optimal_hitting_set(hs); + break; + } + } + } + + struct lt_activity { + maxhs& hs; + lt_activity(maxhs& hs):hs(hs) {} + bool operator()(expr* a, expr* b) const { + unsigned w1 = hs.m_core_activity[hs.m_aux2index.find(a)]; + unsigned w2 = hs.m_core_activity[hs.m_aux2index.find(b)]; + return w1 < w2; + } + }; + + // + // produce the non-optimal hitting set by using the 10% heuristic. + // of most active cores constraints. + // m_asms contains the current core. + // + void find_non_optimal_hitting_set(ptr_vector& hs) { + std::sort(m_asms.begin(), m_asms.end(), lt_activity(*this)); + for (unsigned i = m_asms.size(); i > 9*m_asms.size()/10;) { + --i; + hs.push_back(m_asms[i]); + } + } + + // + // retrieve the next seed that satisfies state of hs. + // state of hs must be satisfiable before optimization is called. + // + lbool next_seed() { + scoped_stopwatch _sw(m_stats.m_aux_sat_time); + TRACE("opt", tout << "\n";); + + // min c_i*(not x_i) for x_i are soft clauses. + // max c_i*x_i for x_i are soft clauses + + m_at_lower_bound = false; + + lbool is_sat = m_hs.compute_upper(); + + if (is_sat == l_true) { + is_sat = m_hs.compute_lower(); + } + if (is_sat == l_true) { + m_at_lower_bound = m_hs.get_upper() == m_hs.get_lower(); + if (m_hs.get_lower() > m_lower) { + m_lower = m_hs.get_lower(); + } + for (unsigned i = 0; i < num_soft(); ++i) { + m_seed[i] = is_active(i) && !m_hs.get_value(i); + } + TRACE("opt", print_seed(tout);); + } + return is_sat; + } + + // + // check assignment returned by HS with the original + // hard constraints. + // + lbool check_subset() { + TRACE("opt", tout << "\n";); + m_asms.reset(); + for (unsigned i = 0; i < num_soft(); ++i) { + if (m_seed[i]) { + m_asms.push_back(m_aux[i].get()); + ensure_active(i); + } + } + return s().check_sat(m_asms.size(), m_asms.c_ptr()); + } + + // + // extend the current assignment to one that + // satisfies as many soft constraints as possible. + // update the upper bound based on this assignment + // + bool grow() { + scoped_stopwatch _sw(m_stats.m_model_expansion_time); + model_ref mdl; + s().get_model(mdl); + for (unsigned i = 0; i < num_soft(); ++i) { + ensure_active(i); + m_seed[i] = false; + } + for (unsigned i = 0; i < m_asms.size(); ++i) { + m_seed[m_aux2index.find(m_asms[i])] = true; + } + + for (unsigned i = 0; i < num_soft(); ++i) { + if (m_seed[i]) { + // already an assumption + } + else if (is_true(mdl, m_soft[i])) { + m_seed[i] = true; + m_asms.push_back(m_aux[i].get()); + } + else { + m_asms.push_back(m_aux[i].get()); + lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); + switch(is_sat) { + case l_undef: + return false; + case l_false: + ++m_stats.m_num_model_expansions_failure; + m_asms.pop_back(); + break; + case l_true: + ++m_stats.m_num_model_expansions_success; + s().get_model(mdl); + m_seed[i] = true; + break; + } + } + } + rational upper(0); + for (unsigned i = 0; i < num_soft(); ++i) { + if (!m_seed[i]) { + upper += m_weights[i]; + } + } + if (upper < m_upper) { + m_upper = upper; + m_hs.set_upper(upper); + m_model = mdl; + m_assignment.reset(); + m_assignment.append(m_seed); + TRACE("opt", + tout << "new upper: " << m_upper << "\n"; + model_smt2_pp(tout, m, *(mdl.get()), 0);); + } + DEBUG_CODE( + for (unsigned i = 0; i < num_soft(); ++i) { + SASSERT(is_true(mdl, m_soft[i]) == m_seed[i]); + }); + + return true; + } + + // + // remove soft constraints from the current core. + // + bool shrink() { + scoped_stopwatch _sw(m_stats.m_core_reduction_time); + m_asms.reset(); + s().get_unsat_core(m_asms); + TRACE("opt", print_asms(tout, m_asms);); + obj_map asm2index; + for (unsigned i = 0; i < m_asms.size(); ++i) { + asm2index.insert(m_asms[i], i); + } + obj_map::iterator it = asm2index.begin(), end = asm2index.end(); + for (; it != end; ++it) { + unsigned i = it->m_value; + if (i < m_asms.size()) { + expr* tmp = m_asms[i]; + expr* back = m_asms.back(); + m_asms[i] = back; + m_asms.pop_back(); + lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); + TRACE("opt", tout << "checking: " << mk_pp(tmp, m) << ": " << is_sat << "\n";); + switch(is_sat) { + case l_true: + ++m_stats.m_num_core_reductions_failure; + // put back literal into core + m_asms.push_back(back); + m_asms[i] = tmp; + break; + case l_false: + // update the core + m_asms.reset(); + ++m_stats.m_num_core_reductions_success; + s().get_unsat_core(m_asms); + TRACE("opt", print_asms(tout, m_asms);); + update_index(asm2index); + break; + case l_undef: + return false; + } + } + } + return true; + } + + void print_asms(std::ostream& out, ptr_vector const& asms) { + for (unsigned j = 0; j < asms.size(); ++j) { + out << mk_pp(asms[j], m) << " "; + } + out << "\n"; + } + + void print_seed(std::ostream& out) { + out << "seed: "; + for (unsigned i = 0; i < num_soft(); ++i) { + out << (m_seed[i]?"1":"0"); + } + out << "\n"; + } + + // + // must include some literal not from asms. + // (furthermore, update upper bound constraint in HS) + // + void block_down() { + uint_set indices; + unsigned_vector c_indices; + for (unsigned i = 0; i < m_asms.size(); ++i) { + indices.insert(m_aux2index.find(m_asms[i])); + } + for (unsigned i = 0; i < num_soft(); ++i) { + if (!indices.contains(i)) { + c_indices.push_back(i); + } + } + m_hs.add_exists_false(c_indices.size(), c_indices.c_ptr()); + } + + // should exclude some literal from core. + void block_up() { + unsigned_vector indices; + for (unsigned i = 0; i < m_asms.size(); ++i) { + unsigned index = m_aux2index.find(m_asms[i]); + m_core_activity[index]++; + indices.push_back(index); + } + m_hs.add_exists_true(indices.size(), indices.c_ptr()); + } + + void update_index(obj_map& asm2index) { + obj_map::iterator it = asm2index.begin(), end = asm2index.end(); + for (; it != end; ++it) { + it->m_value = UINT_MAX; + } + for (unsigned i = 0; i < m_asms.size(); ++i) { + asm2index.find(m_asms[i]) = i; + } + } + + app_ref mk_fresh(sort* s) { + app_ref r(m); + r = m.mk_fresh_const("r", s); + m_c.fm().insert(r->get_decl()); + return r; + } + + bool is_true(model_ref& mdl, expr* e) { + expr_ref val(m); + VERIFY(mdl->eval(e, val)); + return m.is_true(val); + } + + bool is_active(unsigned i) const { + return m_aux_active[i]; + } + + void ensure_active(unsigned i) { + if (!is_active(i)) { + expr_ref fml(m); + fml = m.mk_implies(m_aux[i].get(), m_soft[i]); + s().assert_expr(fml); + m_aux_active[i] = true; + } + } + + }; + + maxsmt_solver_base* mk_maxhs( + maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { + return alloc(maxhs, c, ws, soft); + } + +} diff --git a/src/opt/maxhs.h b/src/opt/maxhs.h new file mode 100644 index 000000000..903354c0e --- /dev/null +++ b/src/opt/maxhs.h @@ -0,0 +1,29 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + maxhs.h + +Abstract: + + HS-max based MaxSAT. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-17 + +Notes: + +--*/ + +#ifndef HS_MAX_H_ +#define HS_MAX_H_ + +#include "maxsmt.h" + +namespace opt { + maxsmt_solver_base* mk_maxhs(maxsat_context& c, + weights_t& ws, expr_ref_vector const& soft); +} +#endif diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp new file mode 100644 index 000000000..7c470f552 --- /dev/null +++ b/src/opt/maxres.cpp @@ -0,0 +1,887 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + maxsres.cpp + +Abstract: + + MaxRes (weighted) max-sat algorithms: + + - mus: max-sat algorithm by Nina and Bacchus, AAAI 2014. + - mus-mss: based on dual refinement of bounds. + + MaxRes is a core-guided approach to maxsat. + MusMssMaxRes extends the core-guided approach by + leveraging both cores and satisfying assignments + to make progress towards a maximal satisfying assignment. + + Given a (minimal) unsatisfiable core for the soft + constraints the approach works like max-res. + Given a (maximal) satisfying subset of the soft constraints + the approach updates the upper bound if the current assignment + improves the current best assignmet. + Furthermore, take the soft constraints that are complements + to the current satisfying subset. + E.g, if F are the hard constraints and + s1,...,sn, t1,..., tm are the soft clauses and + F & s1 & ... & sn is satisfiable, then the complement + of of the current satisfying subset is t1, .., tm. + Update the hard constraint: + F := F & (t1 or ... or tm) + Replace t1, .., tm by m-1 new soft clauses: + t1 & t2, t3 & (t1 or t2), t4 & (t1 or t2 or t3), ..., tn & (t1 or ... t_{n-1}) + Claim: + If k of these soft clauses are satisfied, then k+1 of + the previous soft clauses are satisfied. + If k of these soft clauses are false in the satisfying assignment + for the updated F, then k of the original soft clauses are also false + under the assignment. + In summary: any assignment to the new clauses that satsfies F has the + same cost. + Claim: + If there are no satisfying assignments to F, then the current best assignment + is the optimum. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-20-7 + +Notes: + +--*/ + +#include "solver.h" +#include "maxsmt.h" +#include "maxres.h" +#include "ast_pp.h" +#include "mus.h" +#include "mss.h" +#include "inc_sat_solver.h" +#include "opt_context.h" +#include "pb_decl_plugin.h" +#include "opt_params.hpp" +#include "ast_util.h" +#include "smt_solver.h" + +using namespace opt; + +class maxres : public maxsmt_solver_base { +public: + enum strategy_t { + s_primal, + s_primal_dual + }; +private: + struct stats { + unsigned m_num_cores; + unsigned m_num_cs; + stats() { reset(); } + void reset() { + memset(this, 0, sizeof(*this)); + } + }; + stats m_stats; + expr_ref_vector m_B; + expr_ref_vector m_asms; + expr_ref_vector m_defs; + obj_map m_asm2weight; + ptr_vector m_new_core; + mus m_mus; + mss m_mss; + expr_ref_vector m_trail; + strategy_t m_st; + rational m_max_upper; + model_ref m_csmodel; + unsigned m_correction_set_size; + bool m_found_feasible_optimum; + bool m_hill_climb; // prefer large weight soft clauses for cores + unsigned m_last_index; // last index used during hill-climbing + bool m_add_upper_bound_block; // restrict upper bound with constraint + unsigned m_max_num_cores; // max number of cores per round. + unsigned m_max_core_size; // max core size per round. + bool m_maximize_assignment; // maximize assignment to find MCS + unsigned m_max_correction_set_size;// maximal set of correction set that is tolerated. + bool m_wmax; // Block upper bound using wmax + // this option is disabled if SAT core is used. + bool m_pivot_on_cs; // prefer smaller correction set to core. + bool m_dump_benchmarks; // display benchmarks (into wcnf format) + + std::string m_trace_id; + typedef ptr_vector exprs; + +public: + maxres(maxsat_context& c, + weights_t& ws, expr_ref_vector const& soft, + strategy_t st): + maxsmt_solver_base(c, ws, soft), + m_B(m), m_asms(m), m_defs(m), + m_mus(c.get_solver(), m), + m_mss(c.get_solver(), m), + m_trail(m), + m_st(st), + m_correction_set_size(0), + m_found_feasible_optimum(false), + m_hill_climb(true), + m_last_index(0), + m_add_upper_bound_block(false), + m_max_num_cores(UINT_MAX), + m_max_core_size(3), + m_maximize_assignment(false), + m_max_correction_set_size(3), + m_pivot_on_cs(true) + { + switch(st) { + case s_primal: + m_trace_id = "maxres"; + break; + case s_primal_dual: + m_trace_id = "pd-maxres"; + break; + } + } + + virtual ~maxres() {} + + bool is_literal(expr* l) { + return + is_uninterp_const(l) || + (m.is_not(l, l) && is_uninterp_const(l)); + } + + + + void add_soft(expr* e, rational const& w) { + TRACE("opt", tout << mk_pp(e, m) << "\n";); + expr_ref asum(m), fml(m); + app_ref cls(m); + rational weight(0); + if (m_asm2weight.find(e, weight)) { + weight += w; + m_asm2weight.insert(e, weight); + m_upper += w; + return; + } + if (is_literal(e)) { + asum = e; + } + else { + asum = mk_fresh_bool("soft"); + fml = m.mk_iff(asum, e); + s().assert_expr(fml); + } + new_assumption(asum, w); + m_upper += w; + } + + void new_assumption(expr* e, rational const& w) { + IF_VERBOSE(13, verbose_stream() << "new assumption " << mk_pp(e, m) << " " << w << "\n";); + TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n";); + m_asm2weight.insert(e, w); + m_asms.push_back(e); + m_trail.push_back(e); + } + + void trace() { + trace_bounds(m_trace_id.c_str()); + } + + lbool mus_solver() { + init(); + init_local(); + trace(); + while (m_lower < m_upper) { + TRACE("opt", + display_vec(tout, m_asms); + s().display(tout); + tout << "\n"; + display(tout); + ); + lbool is_sat = check_sat_hill_climb(m_asms); + if (m_cancel) { + return l_undef; + } + switch (is_sat) { + case l_true: + found_optimum(); + return l_true; + case l_false: + is_sat = process_unsat(); + if (is_sat == l_false) { + m_lower = m_upper; + } + if (is_sat == l_undef) { + return is_sat; + } + break; + case l_undef: + return l_undef; + default: + break; + } + } + trace(); + return l_true; + } + + lbool primal_dual_solver() { + init(); + init_local(); + trace(); + exprs cs; + while (m_lower < m_upper) { + lbool is_sat = check_sat_hill_climb(m_asms); + if (m_cancel) { + return l_undef; + } + switch (is_sat) { + case l_true: + get_current_correction_set(cs); + if (cs.empty()) { + m_found_feasible_optimum = m_model.get() != 0; + m_lower = m_upper; + } + else { + process_sat(cs); + } + break; + case l_false: + is_sat = process_unsat(); + if (is_sat == l_false) { + m_lower = m_upper; + } + if (is_sat == l_undef) { + return is_sat; + } + break; + case l_undef: + return l_undef; + default: + break; + } + } + m_lower = m_upper; + trace(); + return l_true; + } + + + lbool check_sat_hill_climb(expr_ref_vector& asms1) { + expr_ref_vector asms(asms1); + lbool is_sat = l_true; + if (m_hill_climb) { + /** + Give preference to cores that have large minmal values. + */ + sort_assumptions(asms); + + m_last_index = std::min(m_last_index, asms.size()-1); + m_last_index = 0; + unsigned index = m_last_index>0?m_last_index-1:0; + m_last_index = 0; + bool first = index > 0; + SASSERT(index < asms.size() || asms.empty()); + while (index < asms.size() && is_sat == l_true) { + while (!first && asms.size() > 20*(index - m_last_index) && index < asms.size()) { + index = next_index(asms, index); + } + first = false; + IF_VERBOSE(3, verbose_stream() << "weight: " << get_weight(asms[0].get()) << " " << get_weight(asms[index-1].get()) << " num soft: " << index << "\n";); + m_last_index = index; + is_sat = check_sat(index, asms.c_ptr()); + } + } + else { + is_sat = check_sat(asms.size(), asms.c_ptr()); + } + return is_sat; + } + + lbool check_sat(unsigned sz, expr* const* asms) { + if (m_st == s_primal_dual && m_c.sat_enabled()) { + rational max_weight = m_upper; + vector weights; + for (unsigned i = 0; i < sz; ++i) { + weights.push_back(get_weight(asms[i])); + } + return inc_sat_check_sat(s(), sz, asms, weights.c_ptr(), max_weight); + } + else { + return s().check_sat(sz, asms); + } + } + + void found_optimum() { + IF_VERBOSE(1, verbose_stream() << "found optimum\n";); + s().get_model(m_model); + SASSERT(is_true(m_asms)); + rational upper(0); + for (unsigned i = 0; i < m_soft.size(); ++i) { + m_assignment[i] = is_true(m_soft[i]); + if (!m_assignment[i]) { + upper += m_weights[i]; + } + } + SASSERT(upper == m_lower); + m_upper = m_lower; + m_found_feasible_optimum = true; + } + + virtual lbool operator()() { + m_defs.reset(); + switch(m_st) { + case s_primal: + return mus_solver(); + case s_primal_dual: + return primal_dual_solver(); + } + return l_undef; + } + + virtual void collect_statistics(statistics& st) const { + st.update("maxres-cores", m_stats.m_num_cores); + st.update("maxres-correction-sets", m_stats.m_num_cs); + } + + lbool get_cores(vector& cores) { + // assume m_s is unsat. + lbool is_sat = l_false; + expr_ref_vector asms(m_asms); + cores.reset(); + exprs core; + while (is_sat == l_false) { + core.reset(); + s().get_unsat_core(core); + //verify_core(core); + model_ref mdl; + get_mus_model(mdl); + is_sat = minimize_core(core); + ++m_stats.m_num_cores; + if (is_sat != l_true) { + IF_VERBOSE(100, verbose_stream() << "(opt.maxres minimization failed)\n";); + break; + } + if (core.empty()) { + IF_VERBOSE(100, verbose_stream() << "(opt.maxres core is empty)\n";); + cores.reset(); + m_lower = m_upper; + return l_true; + } + cores.push_back(core); + if (core.size() >= m_max_core_size) { + break; + } + if (cores.size() >= m_max_num_cores) { + break; + } + remove_soft(core, asms); + is_sat = check_sat_hill_climb(asms); + } + TRACE("opt", + tout << "num cores: " << cores.size() << "\n"; + for (unsigned i = 0; i < cores.size(); ++i) { + display_vec(tout, cores[i]); + } + tout << "num satisfying: " << asms.size() << "\n";); + + return is_sat; + } + + void get_current_correction_set(exprs& cs) { + model_ref mdl; + s().get_model(mdl); + update_assignment(mdl.get()); + get_current_correction_set(mdl.get(), cs); + } + + void get_current_correction_set(model* mdl, exprs& cs) { + cs.reset(); + if (!mdl) return; + for (unsigned i = 0; i < m_asms.size(); ++i) { + if (is_false(mdl, m_asms[i].get())) { + cs.push_back(m_asms[i].get()); + } + } + TRACE("opt", display_vec(tout << "new correction set: ", cs);); + } + + struct compare_asm { + maxres& mr; + compare_asm(maxres& mr):mr(mr) {} + bool operator()(expr* a, expr* b) const { + return mr.get_weight(a) > mr.get_weight(b); + } + }; + + void sort_assumptions(expr_ref_vector& _asms) { + compare_asm comp(*this); + exprs asms(_asms.size(), _asms.c_ptr()); + expr_ref_vector trail(_asms); + std::sort(asms.begin(), asms.end(), comp); + _asms.reset(); + _asms.append(asms.size(), asms.c_ptr()); + DEBUG_CODE( + for (unsigned i = 0; i + 1 < asms.size(); ++i) { + SASSERT(get_weight(asms[i]) >= get_weight(asms[i+1])); + }); + } + + unsigned next_index(expr_ref_vector const& asms, unsigned index) { + if (index < asms.size()) { + rational w = get_weight(asms[index]); + ++index; + for (; index < asms.size() && w == get_weight(asms[index]); ++index); + } + return index; + } + + void process_sat(exprs const& corr_set) { + ++m_stats.m_num_cs; + expr_ref fml(m), tmp(m); + TRACE("opt", display_vec(tout << "corr_set: ", corr_set);); + remove_core(corr_set); + rational w = split_core(corr_set); + cs_max_resolve(corr_set, w); + IF_VERBOSE(2, verbose_stream() << "(opt.maxres.correction-set " << corr_set.size() << ")\n";); + m_csmodel = 0; + m_correction_set_size = 0; + } + + lbool process_unsat() { + vector cores; + lbool is_sat = get_cores(cores); + if (is_sat != l_true) { + return is_sat; + } + if (cores.empty()) { + return l_false; + } + else { + process_unsat(cores); + return l_true; + } + } + + unsigned max_core_size(vector const& cores) { + unsigned result = 0; + for (unsigned i = 0; i < cores.size(); ++i) { + result = std::max(cores[i].size(), result); + } + return result; + } + + void process_unsat(vector const& cores) { + for (unsigned i = 0; i < cores.size(); ++i) { + process_unsat(cores[i]); + } + } + + void update_model(expr* def, expr* value) { + SASSERT(is_uninterp_const(def)); + if (m_csmodel) { + expr_ref val(m); + SASSERT(m_csmodel.get()); + VERIFY(m_csmodel->eval(value, val)); + m_csmodel->register_decl(to_app(def)->get_decl(), val); + } + } + + void process_unsat(exprs const& core) { + IF_VERBOSE(3, verbose_stream() << "(maxres cs model valid: " << (m_csmodel.get() != 0) << " cs size:" << m_correction_set_size << " core: " << core.size() << ")\n";); + expr_ref fml(m); + remove_core(core); + SASSERT(!core.empty()); + rational w = split_core(core); + TRACE("opt", display_vec(tout << "minimized core: ", core);); + IF_VERBOSE(10, display_vec(verbose_stream() << "core: ", core);); + max_resolve(core, w); + fml = mk_not(m, mk_and(m, m_B.size(), m_B.c_ptr())); + s().assert_expr(fml); + m_lower += w; + if (m_st == s_primal_dual) { + m_lower = std::min(m_lower, m_upper); + } + if (m_csmodel.get() && m_correction_set_size > 0) { + // this estimate can overshoot for weighted soft constraints. + --m_correction_set_size; + } + trace(); + if (m_c.num_objectives() == 1 && m_pivot_on_cs && m_csmodel.get() && m_correction_set_size < core.size()) { + exprs cs; + get_current_correction_set(m_csmodel.get(), cs); + m_correction_set_size = cs.size(); + if (m_correction_set_size < core.size()) { + process_sat(cs); + return; + } + } + } + + bool get_mus_model(model_ref& mdl) { + rational w(0); + if (m_c.sat_enabled()) { + // SAT solver core extracts some model + // during unsat core computation. + mdl = 0; + s().get_model(mdl); + } + else { + w = m_mus.get_best_model(mdl); + } + if (mdl.get() && w < m_upper) { + update_assignment(mdl.get()); + } + return 0 != mdl.get(); + } + + lbool minimize_core(exprs& core) { + if (m_c.sat_enabled() || core.empty()) { + return l_true; + } + m_mus.reset(); + for (unsigned i = 0; i < core.size(); ++i) { + m_mus.add_soft(core[i]); + } + unsigned_vector mus_idx; + lbool is_sat = m_mus.get_mus(mus_idx); + if (is_sat != l_true) { + return is_sat; + } + m_new_core.reset(); + for (unsigned i = 0; i < mus_idx.size(); ++i) { + m_new_core.push_back(core[mus_idx[i]]); + } + core.reset(); + core.append(m_new_core); + return l_true; + } + + rational get_weight(expr* e) const { + return m_asm2weight.find(e); + } + + rational split_core(exprs const& core) { + if (core.empty()) return rational(0); + // find the minimal weight: + rational w = get_weight(core[0]); + for (unsigned i = 1; i < core.size(); ++i) { + w = std::min(w, get_weight(core[i])); + } + // add fresh soft clauses for weights that are above w. + for (unsigned i = 0; i < core.size(); ++i) { + rational w2 = get_weight(core[i]); + if (w2 > w) { + rational w3 = w2 - w; + new_assumption(core[i], w3); + } + } + return w; + } + + void display_vec(std::ostream& out, exprs const& exprs) { + display_vec(out, exprs.size(), exprs.c_ptr()); + } + + void display_vec(std::ostream& out, expr_ref_vector const& exprs) { + display_vec(out, exprs.size(), exprs.c_ptr()); + } + + void display_vec(std::ostream& out, unsigned sz, expr* const* args) const { + for (unsigned i = 0; i < sz; ++i) { + out << mk_pp(args[i], m) << " : " << get_weight(args[i]) << " "; + } + out << "\n"; + } + + void display(std::ostream& out) { + for (unsigned i = 0; i < m_asms.size(); ++i) { + expr* a = m_asms[i].get(); + out << mk_pp(a, m) << " : " << get_weight(a) << "\n"; + } + } + + void max_resolve(exprs const& core, rational const& w) { + SASSERT(!core.empty()); + expr_ref fml(m), asum(m); + app_ref cls(m), d(m), dd(m); + m_B.reset(); + m_B.append(core.size(), core.c_ptr()); + // + // d_0 := true + // d_i := b_{i-1} and d_{i-1} for i = 1...sz-1 + // soft (b_i or !d_i) + // == (b_i or !(!b_{i-1} or d_{i-1})) + // == (b_i or b_0 & b_1 & ... & b_{i-1}) + // + // Soft constraint is satisfied if previous soft constraint + // holds or if it is the first soft constraint to fail. + // + // Soundness of this rule can be established using MaxRes + // + for (unsigned i = 1; i < core.size(); ++i) { + expr* b_i = m_B[i-1].get(); + expr* b_i1 = m_B[i].get(); + if (i == 1) { + d = to_app(b_i); + } + else if (i == 2) { + d = m.mk_and(b_i, d); + m_trail.push_back(d); + } + else { + dd = mk_fresh_bool("d"); + fml = m.mk_implies(dd, d); + s().assert_expr(fml); + m_defs.push_back(fml); + fml = m.mk_implies(dd, b_i); + s().assert_expr(fml); + m_defs.push_back(fml); + fml = m.mk_and(d, b_i); + update_model(dd, fml); + d = dd; + } + asum = mk_fresh_bool("a"); + cls = m.mk_or(b_i1, d); + fml = m.mk_implies(asum, cls); + update_model(asum, cls); + new_assumption(asum, w); + s().assert_expr(fml); + m_defs.push_back(fml); + } + } + + // cs is a correction set (a complement of a (maximal) satisfying assignment). + void cs_max_resolve(exprs const& cs, rational const& w) { + if (cs.empty()) return; + TRACE("opt", display_vec(tout << "correction set: ", cs);); + expr_ref fml(m), asum(m); + app_ref cls(m), d(m), dd(m); + m_B.reset(); + m_B.append(cs.size(), cs.c_ptr()); + d = m.mk_false(); + // + // d_0 := false + // d_i := b_{i-1} or d_{i-1} for i = 1...sz-1 + // soft (b_i and d_i) + // == (b_i and (b_0 or b_1 or ... or b_{i-1})) + // + // asm => b_i + // asm => d_{i-1} or b_{i-1} + // d_i => d_{i-1} or b_{i-1} + // + for (unsigned i = 1; i < cs.size(); ++i) { + expr* b_i = m_B[i-1].get(); + expr* b_i1 = m_B[i].get(); + cls = m.mk_or(b_i, d); + if (i > 2) { + d = mk_fresh_bool("d"); + fml = m.mk_implies(d, cls); + update_model(d, cls); + s().assert_expr(fml); + m_defs.push_back(fml); + + } + else { + d = cls; + } + asum = mk_fresh_bool("a"); + fml = m.mk_implies(asum, b_i1); + s().assert_expr(fml); + m_defs.push_back(fml); + fml = m.mk_implies(asum, cls); + s().assert_expr(fml); + m_defs.push_back(fml); + new_assumption(asum, w); + fml = m.mk_and(b_i1, cls); + update_model(asum, fml); + } + fml = m.mk_or(m_B.size(), m_B.c_ptr()); + s().assert_expr(fml); + } + + void update_assignment(model* mdl) { + unsigned correction_set_size = 0; + for (unsigned i = 0; i < m_asms.size(); ++i) { + if (is_false(mdl, m_asms[i].get())) { + ++correction_set_size; + } + } + if (!m_csmodel.get() || correction_set_size < m_correction_set_size) { + m_csmodel = mdl; + m_correction_set_size = correction_set_size; + } + + rational upper(0); + expr_ref tmp(m); + for (unsigned i = 0; i < m_soft.size(); ++i) { + if (!is_true(mdl, m_soft[i])) { + upper += m_weights[i]; + } + } + + if (upper >= m_upper) { + return; + } + m_model = mdl; + + for (unsigned i = 0; i < m_soft.size(); ++i) { + m_assignment[i] = is_true(m_soft[i]); + } + DEBUG_CODE(verify_assignment();); + + m_upper = upper; + trace(); + + add_upper_bound_block(); + } + + void add_upper_bound_block() { + if (!m_add_upper_bound_block) return; + pb_util u(m); + expr_ref_vector nsoft(m); + expr_ref fml(m); + for (unsigned i = 0; i < m_soft.size(); ++i) { + nsoft.push_back(mk_not(m, m_soft[i])); + } + fml = u.mk_lt(nsoft.size(), m_weights.c_ptr(), nsoft.c_ptr(), m_upper); + s().assert_expr(fml); + } + + bool is_true(model* mdl, expr* e) { + expr_ref tmp(m); + VERIFY(mdl->eval(e, tmp)); + return m.is_true(tmp); + } + + bool is_false(model* mdl, expr* e) { + expr_ref tmp(m); + VERIFY(mdl->eval(e, tmp)); + return m.is_false(tmp); + } + + bool is_true(expr* e) { + return is_true(m_model.get(), e); + } + + bool is_true(expr_ref_vector const& es) { + for (unsigned i = 0; i < es.size(); ++i) { + if (!is_true(es[i])) return false; + } + return true; + } + + void remove_soft(exprs const& core, expr_ref_vector& asms) { + for (unsigned i = 0; i < asms.size(); ++i) { + if (core.contains(asms[i].get())) { + asms[i] = asms.back(); + asms.pop_back(); + --i; + } + } + } + + void remove_core(exprs const& core) { + remove_soft(core, m_asms); + } + + virtual void set_cancel(bool f) { + maxsmt_solver_base::set_cancel(f); + m_mus.set_cancel(f); + } + + virtual void updt_params(params_ref& p) { + maxsmt_solver_base::updt_params(p); + opt_params _p(p); + + m_hill_climb = _p.maxres_hill_climb(); + m_add_upper_bound_block = _p.maxres_add_upper_bound_block(); + m_max_num_cores = _p.maxres_max_num_cores(); + m_max_core_size = _p.maxres_max_core_size(); + m_maximize_assignment = _p.maxres_maximize_assignment(); + m_max_correction_set_size = _p.maxres_max_correction_set_size(); + m_pivot_on_cs = _p.maxres_pivot_on_correction_set(); + m_wmax = _p.maxres_wmax(); + m_dump_benchmarks = _p.dump_benchmarks(); + } + + void init_local() { + m_upper.reset(); + m_lower.reset(); + m_trail.reset(); + for (unsigned i = 0; i < m_soft.size(); ++i) { + add_soft(m_soft[i], m_weights[i]); + } + m_max_upper = m_upper; + m_found_feasible_optimum = false; + m_last_index = 0; + add_upper_bound_block(); + m_csmodel = 0; + m_correction_set_size = 0; + } + + virtual void commit_assignment() { + if (m_found_feasible_optimum) { + TRACE("opt", tout << "Committing feasible solution\n"; + tout << m_defs; + tout << m_asms; + ); + for (unsigned i = 0; i < m_defs.size(); ++i) { + s().assert_expr(m_defs[i].get()); + } + for (unsigned i = 0; i < m_asms.size(); ++i) { + s().assert_expr(m_asms[i].get()); + } + } + else { + maxsmt_solver_base::commit_assignment(); + } + } + + void verify_core(exprs const& core) { + IF_VERBOSE(3, verbose_stream() << "verify core\n";); + ref smt_solver = mk_smt_solver(m, m_params, symbol()); + for (unsigned i = 0; i < s().get_num_assertions(); ++i) { + smt_solver->assert_expr(s().get_assertion(i)); + } + for (unsigned i = 0; i < core.size(); ++i) { + smt_solver->assert_expr(core[i]); + } + lbool is_sat = smt_solver->check_sat(0, 0); + if (is_sat == l_true) { + IF_VERBOSE(0, verbose_stream() << "not a core\n";); + } + } + + void verify_assignment() { + IF_VERBOSE(1, verbose_stream() << "verify assignment\n";); + ref smt_solver = mk_smt_solver(m, m_params, symbol()); + for (unsigned i = 0; i < s().get_num_assertions(); ++i) { + smt_solver->assert_expr(s().get_assertion(i)); + } + expr_ref n(m); + for (unsigned i = 0; i < m_soft.size(); ++i) { + n = m_soft[i]; + if (!m_assignment[i]) { + n = mk_not(m, n); + } + smt_solver->assert_expr(n); + } + lbool is_sat = smt_solver->check_sat(0, 0); + if (is_sat == l_false) { + IF_VERBOSE(0, verbose_stream() << "assignment is infeasible\n";); + } + } + +}; + +opt::maxsmt_solver_base* opt::mk_maxres( + maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { + return alloc(maxres, c, ws, soft, maxres::s_primal); +} + +opt::maxsmt_solver_base* opt::mk_primal_dual_maxres( + maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { + return alloc(maxres, c, ws, soft, maxres::s_primal_dual); +} + diff --git a/src/opt/maxres.h b/src/opt/maxres.h new file mode 100644 index 000000000..d8682d4d9 --- /dev/null +++ b/src/opt/maxres.h @@ -0,0 +1,31 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + maxsres.h + +Abstract: + + MaxRes (weighted) max-sat algorithm by Nina and Bacchus, AAAI 2014. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-20-7 + +Notes: + +--*/ + +#ifndef MAXRES_H_ +#define MAXRES_H_ + +namespace opt { + + maxsmt_solver_base* mk_maxres(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); + + maxsmt_solver_base* mk_primal_dual_maxres(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); + +}; + +#endif diff --git a/src/opt/maxsls.cpp b/src/opt/maxsls.cpp new file mode 100644 index 000000000..4e59cfdfd --- /dev/null +++ b/src/opt/maxsls.cpp @@ -0,0 +1,73 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + maxsls.cpp + +Abstract: + + Weighted SLS MAXSAT module + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-17 + +Notes: + +--*/ + +#include "maxsls.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" +#include "opt_context.h" +#include "inc_sat_solver.h" + + +namespace opt { + + class sls : public maxsmt_solver_base { + public: + sls(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): + maxsmt_solver_base(c, ws, soft) { + } + virtual ~sls() {} + lbool operator()() { + IF_VERBOSE(1, verbose_stream() << "(opt.sls)\n";); + init(); + enable_sls(true); + lbool is_sat = check(); + if (is_sat == l_true) { + s().get_model(m_model); + m_upper.reset(); + for (unsigned i = 0; i < m_soft.size(); ++i) { + expr_ref tmp(m); + m_model->eval(m_soft[i], tmp, true); + m_assignment[i] = m.is_true(tmp); + if (!m_assignment[i]) { + m_upper += m_weights[i]; + } + } + } + return is_sat; + } + + lbool check() { + if (m_c.sat_enabled()) { + return inc_sat_check_sat( + s(), m_soft.size(), m_soft.c_ptr(), m_weights.c_ptr(), m_upper); + } + else { + return s().check_sat(0, 0); + } + } + + }; + + maxsmt_solver_base* mk_sls( + maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { + return alloc(sls, c, ws, soft); + } + + +}; diff --git a/src/opt/maxsls.h b/src/opt/maxsls.h new file mode 100644 index 000000000..b23512df4 --- /dev/null +++ b/src/opt/maxsls.h @@ -0,0 +1,35 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + maxsls.h + +Abstract: + + Weighted SLS MAXSAT module + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-17 + +Notes: + + Partial, one-round SLS optimizer. Finds the first + local maximum given a resource bound and returns. + +--*/ +#ifndef OPT_SLS_MAX_SAT_H_ +#define OPT_SLS_MAX_SAT_H_ + +#include "maxsmt.h" + +namespace opt { + + + maxsmt_solver_base* mk_sls(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); + + +}; + +#endif diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp new file mode 100644 index 000000000..6b518edfa --- /dev/null +++ b/src/opt/maxsmt.cpp @@ -0,0 +1,316 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + maxsmt.cpp + +Abstract: + + MaxSMT optimization context. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-7 + +Notes: + +--*/ + +#include +#include "maxsmt.h" +#include "fu_malik.h" +#include "maxres.h" +#include "maxhs.h" +#include "bcd2.h" +#include "wmax.h" +#include "maxsls.h" +#include "ast_pp.h" +#include "uint_set.h" +#include "opt_context.h" +#include "theory_wmaxsat.h" +#include "ast_util.h" +#include "pb_decl_plugin.h" + + +namespace opt { + + maxsmt_solver_base::maxsmt_solver_base( + maxsat_context& c, vector const& ws, expr_ref_vector const& soft): + m(c.get_manager()), + m_c(c), + m_cancel(false), + m_soft(soft), + m_weights(ws), + m_assertions(m) { + c.get_base_model(m_model); + SASSERT(m_model); + updt_params(c.params()); + } + + void maxsmt_solver_base::updt_params(params_ref& p) { + m_params.copy(p); + } + + solver& maxsmt_solver_base::s() { + return m_c.get_solver(); + } + + void maxsmt_solver_base::commit_assignment() { + expr_ref tmp(m); + rational k(0); + for (unsigned i = 0; i < m_soft.size(); ++i) { + if (get_assignment(i)) { + k += m_weights[i]; + } + } + pb_util pb(m); + tmp = pb.mk_ge(m_weights.size(), m_weights.c_ptr(), m_soft.c_ptr(), k); + TRACE("opt", tout << tmp << "\n";); + s().assert_expr(tmp); + } + + void maxsmt_solver_base::init() { + m_lower.reset(); + m_upper.reset(); + m_assignment.reset(); + for (unsigned i = 0; i < m_weights.size(); ++i) { + expr_ref val(m); + VERIFY(m_model->eval(m_soft[i], val)); + m_assignment.push_back(m.is_true(val)); + if (!m_assignment.back()) { + m_upper += m_weights[i]; + } + } + + TRACE("opt", + tout << "upper: " << m_upper << " assignments: "; + for (unsigned i = 0; i < m_weights.size(); ++i) { + tout << (m_assignment[i]?"T":"F"); + } + tout << "\n";); + } + + void maxsmt_solver_base::set_mus(bool f) { + params_ref p; + p.set_bool("minimize_core", f); + // p.set_bool("minimize_core_partial", f); + s().updt_params(p); + } + + void maxsmt_solver_base::enable_sls(bool force) { + m_c.enable_sls(force); + } + + app* maxsmt_solver_base::mk_fresh_bool(char const* name) { + app* result = m.mk_fresh_const(name, m.mk_bool_sort()); + m_c.fm().insert(result->get_decl()); + return result; + } + + smt::theory_wmaxsat* maxsmt_solver_base::get_wmax_theory() const { + smt::theory_id th_id = m.get_family_id("weighted_maxsat"); + smt::theory* th = m_c.smt_context().get_theory(th_id); + if (th) { + return dynamic_cast(th); + } + else { + return 0; + } + } + + smt::theory_wmaxsat* maxsmt_solver_base::ensure_wmax_theory() { + smt::theory_wmaxsat* wth = get_wmax_theory(); + if (wth) { + wth->reset_local(); + } + else { + wth = alloc(smt::theory_wmaxsat, m, m_c.fm()); + m_c.smt_context().register_plugin(wth); + } + return wth; + } + + maxsmt_solver_base::scoped_ensure_theory::scoped_ensure_theory(maxsmt_solver_base& s) { + m_wth = s.ensure_wmax_theory(); + } + maxsmt_solver_base::scoped_ensure_theory::~scoped_ensure_theory() { + //m_wth->reset_local(); + } + smt::theory_wmaxsat& maxsmt_solver_base::scoped_ensure_theory::operator()() { return *m_wth; } + + void maxsmt_solver_base::trace_bounds(char const * solver) { + IF_VERBOSE(1, + rational l = m_adjust_value(m_lower); + rational u = m_adjust_value(m_upper); + if (l > u) std::swap(l, u); + verbose_stream() << "(opt." << solver << " [" << l << ":" << u << "])\n";); + } + + + + maxsmt::maxsmt(maxsat_context& c): + m(c.get_manager()), m_c(c), m_cancel(false), + m_soft_constraints(m), m_answer(m) {} + + lbool maxsmt::operator()() { + lbool is_sat; + m_msolver = 0; + symbol const& maxsat_engine = m_c.maxsat_engine(); + IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); + TRACE("opt", tout << "maxsmt\n";); + if (m_soft_constraints.empty()) { + TRACE("opt", tout << "no constraints\n";); + m_msolver = 0; + is_sat = s().check_sat(0, 0); + } + else if (maxsat_engine == symbol("maxres")) { + m_msolver = mk_maxres(m_c, m_weights, m_soft_constraints); + } + else if (maxsat_engine == symbol("pd-maxres")) { + m_msolver = mk_primal_dual_maxres(m_c, m_weights, m_soft_constraints); + } + else if (maxsat_engine == symbol("bcd2")) { + m_msolver = mk_bcd2(m_c, m_weights, m_soft_constraints); + } + else if (maxsat_engine == symbol("maxhs")) { + m_msolver = mk_maxhs(m_c, m_weights, m_soft_constraints); + } + else if (maxsat_engine == symbol("sls")) { + // NB: this is experimental one-round version of SLS + m_msolver = mk_sls(m_c, m_weights, m_soft_constraints); + } + else if (is_maxsat_problem(m_weights) && maxsat_engine == symbol("fu_malik")) { + m_msolver = mk_fu_malik(m_c, m_weights, m_soft_constraints); + } + else { + if (maxsat_engine != symbol::null && maxsat_engine != symbol("wmax")) { + warning_msg("solver %s is not recognized, using default 'wmax'", + maxsat_engine.str().c_str()); + } + m_msolver = mk_wmax(m_c, m_weights, m_soft_constraints); + } + + if (m_msolver) { + m_msolver->updt_params(m_params); + m_msolver->set_adjust_value(m_adjust_value); + is_sat = (*m_msolver)(); + if (is_sat != l_false) { + m_msolver->get_model(m_model); + } + } + + IF_VERBOSE(1, verbose_stream() << "is-sat: " << is_sat << "\n"; + if (is_sat == l_true) { + verbose_stream() << "Satisfying soft constraints\n"; + display_answer(verbose_stream()); + }); + + DEBUG_CODE(if (is_sat == l_true) verify_assignment();); + + return is_sat; + } + + void maxsmt::verify_assignment() { + // TBD: have to use a different solver + // because we don't push local scope any longer. + return; + } + + bool maxsmt::get_assignment(unsigned idx) const { + if (m_msolver) { + return m_msolver->get_assignment(idx); + } + else { + return true; + } + } + + rational maxsmt::get_lower() const { + rational r = m_lower; + if (m_msolver) { + rational q = m_msolver->get_lower(); + if (q > r) r = q; + } + return m_adjust_value(r); + } + + rational maxsmt::get_upper() const { + rational r = m_upper; + if (m_msolver) { + rational q = m_msolver->get_upper(); + if (q < r) r = q; + } + return m_adjust_value(r); + } + + void maxsmt::update_lower(rational const& r) { + m_lower = r; + } + + void maxsmt::update_upper(rational const& r) { + m_upper = r; + } + + void maxsmt::get_model(model_ref& mdl) { + mdl = m_model.get(); + } + + void maxsmt::commit_assignment() { + if (m_msolver) { + m_msolver->commit_assignment(); + } + } + + void maxsmt::add(expr* f, rational const& w) { + TRACE("opt", tout << mk_pp(f, m) << " weight: " << w << "\n";); + SASSERT(m.is_bool(f)); + SASSERT(w.is_pos()); + m_soft_constraints.push_back(f); + m_weights.push_back(w); + m_upper += w; + } + + void maxsmt::display_answer(std::ostream& out) const { + for (unsigned i = 0; i < m_soft_constraints.size(); ++i) { + out << mk_pp(m_soft_constraints[i], m) + << (get_assignment(i)?" |-> true\n":" |-> false\n"); + } + } + + void maxsmt::set_cancel(bool f) { + m_cancel = f; + + if (m_msolver) { + m_msolver->set_cancel(f); + } + } + + bool maxsmt::is_maxsat_problem(vector const& ws) const { + for (unsigned i = 0; i < ws.size(); ++i) { + if (!ws[i].is_one()) { + return false; + } + } + return true; + } + + void maxsmt::updt_params(params_ref& p) { + m_params.append(p); + if (m_msolver) { + m_msolver->updt_params(p); + } + } + + void maxsmt::collect_statistics(statistics& st) const { + if (m_msolver) { + m_msolver->collect_statistics(st); + } + } + + solver& maxsmt::s() { + return m_c.get_solver(); + } + + +}; diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h new file mode 100644 index 000000000..8d290a640 --- /dev/null +++ b/src/opt/maxsmt.h @@ -0,0 +1,154 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + maxsmt.h + +Abstract: + + MaxSMT optimization context. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-7 + +Notes: + +--*/ +#ifndef OPT_MAXSMT_H_ +#define OPT_MAXSMT_H_ + +#include"ast.h" +#include"params.h" +#include"solver.h" +#include"filter_model_converter.h" +#include"statistics.h" +#include"smt_context.h" +#include"smt_theory.h" +#include"theory_wmaxsat.h" +#include"opt_solver.h" + +namespace opt { + + typedef vector const weights_t; + + class maxsat_context; + + class maxsmt_solver { + protected: + adjust_value m_adjust_value; + public: + virtual ~maxsmt_solver() {} + virtual lbool operator()() = 0; + virtual rational get_lower() const = 0; + virtual rational get_upper() const = 0; + virtual bool get_assignment(unsigned index) const = 0; + virtual void set_cancel(bool f) = 0; + virtual void collect_statistics(statistics& st) const = 0; + virtual void get_model(model_ref& mdl) = 0; + virtual void updt_params(params_ref& p) = 0; + void set_adjust_value(adjust_value& adj) { m_adjust_value = adj; } + + }; + + // --------------------------------------------- + // base class with common utilities used + // by maxsmt solvers + // + class maxsmt_solver_base : public maxsmt_solver { + protected: + ast_manager& m; + maxsat_context& m_c; + volatile bool m_cancel; + const expr_ref_vector m_soft; + vector m_weights; + expr_ref_vector m_assertions; + rational m_lower; + rational m_upper; + model_ref m_model; + svector m_assignment; // truth assignment to soft constraints + params_ref m_params; // config + + public: + maxsmt_solver_base(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); + + virtual ~maxsmt_solver_base() {} + virtual rational get_lower() const { return m_lower; } + virtual rational get_upper() const { return m_upper; } + virtual bool get_assignment(unsigned index) const { return m_assignment[index]; } + virtual void set_cancel(bool f) { m_cancel = f; if (f) s().cancel(); else s().reset_cancel(); } + virtual void collect_statistics(statistics& st) const { } + virtual void get_model(model_ref& mdl) { mdl = m_model.get(); } + virtual void commit_assignment(); + void set_model() { s().get_model(m_model); } + virtual void updt_params(params_ref& p); + solver& s(); + void init(); + void set_mus(bool f); + app* mk_fresh_bool(char const* name); + + class smt::theory_wmaxsat* get_wmax_theory() const; + smt::theory_wmaxsat* ensure_wmax_theory(); + class scoped_ensure_theory { + smt::theory_wmaxsat* m_wth; + public: + scoped_ensure_theory(maxsmt_solver_base& s); + ~scoped_ensure_theory(); + smt::theory_wmaxsat& operator()(); + }; + + + protected: + void enable_sls(bool force); + void trace_bounds(char const* solver); + + }; + + /** + Takes solver with hard constraints added. + Returns modified soft constraints that are maximal assignments. + */ + + class maxsmt { + ast_manager& m; + maxsat_context& m_c; + scoped_ptr m_msolver; + volatile bool m_cancel; + expr_ref_vector m_soft_constraints; + expr_ref_vector m_answer; + vector m_weights; + rational m_lower; + rational m_upper; + adjust_value m_adjust_value; + model_ref m_model; + params_ref m_params; + public: + maxsmt(maxsat_context& c); + lbool operator()(); + void set_cancel(bool f); + void updt_params(params_ref& p); + void add(expr* f, rational const& w); + void set_adjust_value(adjust_value& adj) { m_adjust_value = adj; } + unsigned size() const { return m_soft_constraints.size(); } + expr* operator[](unsigned idx) const { return m_soft_constraints[idx]; } + rational weight(unsigned idx) const { return m_weights[idx]; } + void commit_assignment(); + rational get_value() const; + rational get_lower() const; + rational get_upper() const; + void update_lower(rational const& r); + void update_upper(rational const& r); + void get_model(model_ref& mdl); + bool get_assignment(unsigned index) const; + void display_answer(std::ostream& out) const; + void collect_statistics(statistics& st) const; + private: + bool is_maxsat_problem(weights_t& ws) const; + void verify_assignment(); + solver& s(); + }; + +}; + +#endif diff --git a/src/opt/mss.cpp b/src/opt/mss.cpp new file mode 100644 index 000000000..9d44180a6 --- /dev/null +++ b/src/opt/mss.cpp @@ -0,0 +1,288 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + mss.cpp + +Abstract: + + MSS/MCS extraction. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-2-8 + +Notes: + + +--*/ + +#include "solver.h" +#include "mss.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" + +namespace opt { + + + mss::mss(solver& s, ast_manager& m): m_s(s), m(m), m_cancel(false) { + } + + mss::~mss() { + } + + bool mss::check_result() { + lbool is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); + if (is_sat == l_undef) return true; + SASSERT(m_mss.empty() || is_sat == l_true); + if (is_sat == l_false) return false; + expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); + for (; it != end; ++it) { + m_mss.push_back(*it); + is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); + m_mss.pop_back(); + if (is_sat == l_undef) return true; + SASSERT(is_sat == l_false); + if (is_sat == l_true) return false; + } + return true; + } + + void mss::initialize(exprs& literals) { + expr* n; + expr_set lits, core_lits; + for (unsigned i = 0; i < literals.size(); ++i) { + n = literals[i]; + lits.insert(n); + m.is_not(n, n); + if (!is_uninterp_const(n)) { + throw default_exception("arguments have to be uninterpreted literals"); + } + } + exprs rest_core; + expr_ref tmp(m); + // + // the last core is a dummy core. It contains literals that + // did not occur in previous cores and did not evaluate to true + // in the current model. + // + for (unsigned i = 0; i < m_cores.size(); ++i) { + exprs const& core = m_cores[i]; + for (unsigned j = 0; j < core.size(); ++j) { + expr* n = core[j]; + if (!core_lits.contains(n)) { + core_lits.insert(n); + VERIFY(m_model->eval(n, tmp)); + if (m.is_true(tmp)) { + add_mss(n); + } + else { + m_todo.push_back(n); + } + } + } + } + for (unsigned i = 0; i < literals.size(); ++i) { + expr* n = literals[i]; + if (!core_lits.contains(n)) { + VERIFY(m_model->eval(n, tmp)); + if (m.is_true(tmp)) { + m_mss.push_back(n); + } + else { + rest_core.push_back(n); + core_lits.insert(n); + m_todo.push_back(n); + } + } + } + m_cores.push_back(rest_core); + } + + void mss::add_mss(expr* n) { + if (!m_mss_set.contains(n)) { + m_mss_set.insert(n); + m_mss.push_back(n); + } + } + + void mss::update_core(exprs& core) { + unsigned j = 0; + for (unsigned i = 0; i < core.size(); ++i) { + expr* n = core[i]; + if (!m_mss_set.contains(n)) { + if (i != j) { + core[j] = core[i]; + } + ++j; + } + } + core.resize(j); + } + + void mss::update_mss() { + expr_ref tmp(m); + unsigned j = 0; + for (unsigned i = 0; i < m_todo.size(); ++i) { + expr* n = m_todo[i]; + SASSERT(!m_mss_set.contains(n)); + if (m_mcs.contains(n)) { + continue; // remove from cores. + } + VERIFY(m_model->eval(n, tmp)); + if (m.is_true(tmp)) { + add_mss(n); + } + else { + if (j != i) { + m_todo[j] = m_todo[i]; + } + ++j; + } + } + m_todo.resize(j); + } + + lbool mss::operator()(model* initial_model, vector const& _cores, exprs& literals, exprs& mcs) { + m_mss.reset(); + m_todo.reset(); + m_model = initial_model; + m_cores.reset(); + SASSERT(m_model); + m_cores.append(_cores); + initialize(literals); + TRACE("opt", + display_vec(tout << "lits: ", literals.size(), literals.c_ptr()); + display(tout);); + lbool is_sat = l_true; + for (unsigned i = 0; is_sat == l_true && i < m_cores.size(); ++i) { + bool has_mcs = false; + bool is_last = i + 1 < m_cores.size(); + SASSERT(check_invariant()); + update_core(m_cores[i]); // remove members of mss + is_sat = process_core(1, m_cores[i], has_mcs, is_last); + } + if (is_sat == l_true) { + SASSERT(check_invariant()); + TRACE("opt", display(tout);); + literals.reset(); + literals.append(m_mss); + mcs.reset(); + expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); + for (; it != end; ++it) { + mcs.push_back(*it); + } + SASSERT(check_result()); + } + m_mcs.reset(); + m_mss_set.reset(); + IF_VERBOSE(2, display_vec(verbose_stream() << "mcs: ", mcs.size(), mcs.c_ptr());); + return is_sat; + } + + + // + // at least one literal in core is false in current model. + // pick literals in core that are not yet in mss. + // + lbool mss::process_core(unsigned sz, exprs& core, bool& has_mcs, bool is_last) { + SASSERT(sz > 0); + if (core.empty()) { + return l_true; + } + if (m_cancel) { + return l_undef; + } + if (sz == 1 && core.size() == 1 && is_last && !has_mcs) { + // there has to be at least one false + // literal in the core. + TRACE("opt", tout << "mcs: " << mk_pp(core[0], m) << "\n";); + m_mcs.insert(core[0]); + return l_true; + } + sz = std::min(sz, core.size()); + TRACE("opt", display_vec(tout << "process (total " << core.size() << ") :", sz, core.c_ptr());); + unsigned sz_save = m_mss.size(); + m_mss.append(sz, core.c_ptr()); + lbool is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); + IF_VERBOSE(3, display_vec(verbose_stream() << "mss: ", m_mss.size(), m_mss.c_ptr());); + m_mss.resize(sz_save); + switch (is_sat) { + case l_true: + m_s.get_model(m_model); + update_mss(); + DEBUG_CODE( + for (unsigned i = 0; i < sz; ++i) { + SASSERT(m_mss_set.contains(core[i])); + }); + update_core(core); + return process_core(2*sz, core, has_mcs, is_last); + case l_false: + if (sz == 1) { + has_mcs = true; + m_mcs.insert(core[0]); + core[0] = core.back(); + core.pop_back(); + } + else { + exprs core2; + core2.append(core.size()-sz, core.c_ptr()+sz); + core.resize(sz); + is_sat = process_core(sz, core2, has_mcs, false); + if (is_sat != l_true) { + return is_sat; + } + update_core(core); + } + return process_core(1, core, has_mcs, is_last); + case l_undef: + return l_undef; + } + + return l_true; + } + + void mss::display_vec(std::ostream& out, unsigned sz, expr* const* args) const { + for (unsigned i = 0; i < sz; ++i) { + out << mk_pp(args[i], m) << " "; + } + out << "\n"; + } + + void mss::display(std::ostream& out) const { + for (unsigned i = 0; i < m_cores.size(); ++i) { + display_vec(out << "core: ", m_cores[i].size(), m_cores[i].c_ptr()); + } + expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); + out << "mcs:\n"; + for (; it != end; ++it) { + out << mk_pp(*it, m) << "\n"; + } + out << "\n"; + out << "mss:\n"; + for (unsigned i = 0; i < m_mss.size(); ++i) { + out << mk_pp(m_mss[i], m) << "\n"; + } + out << "\n"; + if (m_model) { + model_smt2_pp(out, m, *(m_model.get()), 0); + } + } + + bool mss::check_invariant() const { + if (!m_model) return true; + expr_ref tmp(m); + for (unsigned i = 0; i < m_mss.size(); ++i) { + expr* n = m_mss[i]; + VERIFY(m_model->eval(n, tmp)); + CTRACE("opt", !m.is_true(tmp), tout << mk_pp(n, m) << " |-> " << mk_pp(tmp, m) << "\n";); + SASSERT(!m.is_false(tmp)); + } + return true; + } +} + + + + diff --git a/src/opt/mss.h b/src/opt/mss.h new file mode 100644 index 000000000..ba9cda95d --- /dev/null +++ b/src/opt/mss.h @@ -0,0 +1,59 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + mss.h + +Abstract: + + Maximal satisfying subset/minimal correction sets: MSS/MCS + +Author: + + Nikolaj Bjorner (nbjorner) 2014-2-8 + +Notes: + +--*/ +#ifndef MSS_H_ +#define MSS_H_ + +namespace opt { + class mss { + solver& m_s; + ast_manager& m; + volatile bool m_cancel; + typedef ptr_vector exprs; + typedef obj_hashtable expr_set; + exprs m_mss; + expr_set m_mcs; + expr_set m_mss_set; + vector m_cores; + exprs m_todo; + model_ref m_model; + public: + mss(solver& s, ast_manager& m); + ~mss(); + + lbool operator()(model* initial_model, vector const& cores, exprs& literals, exprs& mcs); + + void set_cancel(bool f) { m_cancel = f; } + + void get_model(model_ref& mdl) { mdl = m_model; } + + private: + void initialize(exprs& literals); + bool check_result(); + void add_mss(expr* n); + void update_mss(); + void update_core(exprs& core); + lbool process_core(unsigned sz, exprs& core, bool& has_mcs, bool is_last); + void display(std::ostream& out) const; + void display_vec(std::ostream& out, unsigned sz, expr* const* args) const; + bool check_invariant() const; + }; + +}; + +#endif diff --git a/src/opt/mus.cpp b/src/opt/mus.cpp new file mode 100644 index 000000000..27361a4f9 --- /dev/null +++ b/src/opt/mus.cpp @@ -0,0 +1,233 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + mus.cpp + +Abstract: + + MUS extraction. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-20-7 + +Notes: + + +--*/ + +#include "solver.h" +#include "smt_literal.h" +#include "mus.h" +#include "ast_pp.h" +#include "ast_util.h" + +using namespace opt; + +// + +struct mus::imp { + solver& m_s; + ast_manager& m; + expr_ref_vector m_cls2expr; + obj_map m_expr2cls; + volatile bool m_cancel; + model_ref m_model; + expr_ref_vector m_soft; + vector m_weights; + rational m_weight; + + imp(solver& s, ast_manager& m): + m_s(s), m(m), m_cls2expr(m), m_cancel(false), m_soft(m) + {} + + void reset() { + m_cls2expr.reset(); + m_expr2cls.reset(); + } + + void set_cancel(bool f) { + m_cancel = f; + } + + + unsigned add_soft(expr* cls) { + SASSERT(is_uninterp_const(cls) || + (m.is_not(cls) && is_uninterp_const(to_app(cls)->get_arg(0)))); + unsigned idx = m_cls2expr.size(); + m_expr2cls.insert(cls, idx); + m_cls2expr.push_back(cls); + TRACE("opt", tout << idx << ": " << mk_pp(cls, m) << "\n"; + display_vec(tout, m_cls2expr);); + return idx; + } + + lbool get_mus(unsigned_vector& mus) { + // SASSERT: mus does not have duplicates. + m_model.reset(); + unsigned_vector core; + for (unsigned i = 0; i < m_cls2expr.size(); ++i) { + core.push_back(i); + } + if (core.size() == 1) { + mus.push_back(core.back()); + return l_true; + } + mus.reset(); + expr_ref_vector assumptions(m); + ptr_vector core_exprs; + while (!core.empty()) { + IF_VERBOSE(2, verbose_stream() << "(opt.mus reducing core: " << core.size() << " new core: " << mus.size() << ")\n";); + unsigned cls_id = core.back(); + TRACE("opt", + display_vec(tout << "core: ", core); + display_vec(tout << "mus: ", mus); + ); + core.pop_back(); + expr* cls = m_cls2expr[cls_id].get(); + expr_ref not_cls(m); + not_cls = mk_not(m, cls); + unsigned sz = assumptions.size(); + assumptions.push_back(not_cls); + add_core(core, assumptions); + lbool is_sat = m_s.check_sat(assumptions.size(), assumptions.c_ptr()); + assumptions.resize(sz); + switch (is_sat) { + case l_undef: + return is_sat; + case l_true: + assumptions.push_back(cls); + mus.push_back(cls_id); + update_model(); + break; + default: + core_exprs.reset(); + m_s.get_unsat_core(core_exprs); + if (!core_exprs.contains(not_cls)) { + // core := core_exprs \ mus + core.reset(); + for (unsigned i = 0; i < core_exprs.size(); ++i) { + cls = core_exprs[i]; + cls_id = m_expr2cls.find(cls); + if (!mus.contains(cls_id)) { + core.push_back(cls_id); + } + } + TRACE("opt", display_vec(tout << "core exprs:", core_exprs); + display_vec(tout << "core:", core); + display_vec(tout << "mus:", mus); + ); + + } + break; + } + } +#if 0 + DEBUG_CODE( + assumptions.reset(); + for (unsigned i = 0; i < mus.size(); ++i) { + assumptions.push_back(m_cls2expr[mus[i]].get()); + } + lbool is_sat = m_s.check_sat(assumptions.size(), assumptions.c_ptr()); + SASSERT(is_sat == l_false); + ); +#endif + return l_true; + } + + void add_core(unsigned_vector const& core, expr_ref_vector& assumptions) { + for (unsigned i = 0; i < core.size(); ++i) { + assumptions.push_back(m_cls2expr[core[i]].get()); + } + } + + template + void display_vec(std::ostream& out, T const& v) const { + for (unsigned i = 0; i < v.size(); ++i) { + out << v[i] << " "; + } + out << "\n"; + } + + void display_vec(std::ostream& out, expr_ref_vector const& v) const { + for (unsigned i = 0; i < v.size(); ++i) + out << mk_pp(v[i], m) << " "; + out << "\n"; + } + + + void display_vec(std::ostream& out, ptr_vector const& v) const { + for (unsigned i = 0; i < v.size(); ++i) + out << mk_pp(v[i], m) << " "; + out << "\n"; + } + + void set_soft(unsigned sz, expr* const* soft, rational const* weights) { + m_model.reset(); + m_weight.reset(); + m_soft.append(sz, soft); + m_weights.append(sz, weights); + for (unsigned i = 0; i < sz; ++i) { + m_weight += weights[i]; + } + } + + void update_model() { + if (m_soft.empty()) return; + model_ref mdl; + expr_ref tmp(m); + m_s.get_model(mdl); + rational w; + for (unsigned i = 0; i < m_soft.size(); ++i) { + mdl->eval(m_soft[i].get(), tmp); + if (!m.is_true(tmp)) { + w += m_weights[i]; + } + } + if (w < m_weight || !m_model.get()) { + m_model = mdl; + m_weight = w; + } + } + + rational get_best_model(model_ref& mdl) { + mdl = m_model; + return m_weight; + } + + +}; + +mus::mus(solver& s, ast_manager& m) { + m_imp = alloc(imp, s, m); +} + +mus::~mus() { + dealloc(m_imp); +} + +unsigned mus::add_soft(expr* cls) { + return m_imp->add_soft(cls); +} + +lbool mus::get_mus(unsigned_vector& mus) { + return m_imp->get_mus(mus); +} + +void mus::set_cancel(bool f) { + m_imp->set_cancel(f); +} + +void mus::reset() { + m_imp->reset(); +} + +void mus::set_soft(unsigned sz, expr* const* soft, rational const* weights) { + m_imp->set_soft(sz, soft, weights); +} + +rational mus::get_best_model(model_ref& mdl) { + return m_imp->get_best_model(mdl); +} diff --git a/src/opt/mus.h b/src/opt/mus.h new file mode 100644 index 000000000..b58a303e8 --- /dev/null +++ b/src/opt/mus.h @@ -0,0 +1,57 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + mus.h + +Abstract: + + Basic MUS extraction + +Author: + + Nikolaj Bjorner (nbjorner) 2014-20-7 + +Notes: + +--*/ +#ifndef MUS_H_ +#define MUS_H_ + +namespace opt { + class mus { + struct imp; + imp * m_imp; + public: + mus(solver& s, ast_manager& m); + ~mus(); + /** + Add soft constraint. + + Assume that the solver context enforces that + cls is equivalent to a disjunction of args. + Assume also that cls is a literal. + */ + unsigned add_soft(expr* cls); + + lbool get_mus(unsigned_vector& mus); + + void reset(); + + void set_cancel(bool f); + + /** + Instrument MUS extraction to also provide the minimal + penalty model, if any is found. + The minimal penalty model has the least weight for the + supplied soft constraints. + */ + void set_soft(unsigned sz, expr* const* soft, rational const* weights); + rational get_best_model(model_ref& mdl); + + }; + +}; + +#endif diff --git a/src/opt/opt_cmds.cpp b/src/opt/opt_cmds.cpp new file mode 100644 index 000000000..7041265de --- /dev/null +++ b/src/opt/opt_cmds.cpp @@ -0,0 +1,342 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + opt_cmds.cpp + +Abstract: + Commands for optimization benchmarks + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-14 + +Notes: + + TODO: + + - Add appropriate statistics tracking to opt::context + + - Deal with push/pop (later) + +--*/ +#include "opt_cmds.h" +#include "cmd_context.h" +#include "ast_pp.h" +#include "opt_context.h" +#include "cancel_eh.h" +#include "scoped_ctrl_c.h" +#include "scoped_timer.h" +#include "parametric_cmd.h" +#include "opt_params.hpp" +#include "model_smt2_pp.h" + +static opt::context& get_opt(cmd_context& cmd) { + if (!cmd.get_opt()) { + cmd.set_opt(alloc(opt::context, cmd.m())); + } + return dynamic_cast(*cmd.get_opt()); +} + + +class assert_soft_cmd : public parametric_cmd { + unsigned m_idx; + expr* m_formula; + +public: + assert_soft_cmd(): + parametric_cmd("assert-soft"), + m_idx(0), + m_formula(0) + {} + + virtual ~assert_soft_cmd() { + } + + virtual void reset(cmd_context & ctx) { + m_idx = 0; + m_formula = 0; + } + + virtual char const * get_usage() const { return " [:weight ] [:id ]"; } + virtual char const * get_main_descr() const { return "assert soft constraint with optional weight and identifier"; } + + // command invocation + virtual void prepare(cmd_context & ctx) { + reset(ctx); + } + + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + if (m_idx == 0) return CPK_EXPR; + return parametric_cmd::next_arg_kind(ctx); + } + + virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + p.insert("weight", CPK_NUMERAL, "(default: 1) penalty of not satisfying constraint."); + p.insert("id", CPK_SYMBOL, "(default: null) partition identifier for soft constraints."); + } + + virtual void set_next_arg(cmd_context & ctx, expr * t) { + SASSERT(m_idx == 0); + if (!ctx.m().is_bool(t)) { + throw cmd_exception("Invalid type for expression. Expected Boolean type."); + } + m_formula = t; + ++m_idx; + } + + virtual void failure_cleanup(cmd_context & ctx) { + reset(ctx); + } + + virtual void execute(cmd_context & ctx) { + symbol w("weight"); + rational weight = ps().get_rat(symbol("weight"), rational(0)); + if (weight.is_zero()) { + weight = rational::one(); + } + symbol id = ps().get_sym(symbol("id"), symbol::null); + get_opt(ctx).add_soft_constraint(m_formula, weight, id); + reset(ctx); + } + + virtual void finalize(cmd_context & ctx) { + } + +}; + +class min_maximize_cmd : public cmd { + bool m_is_max; + +public: + min_maximize_cmd(bool is_max): + cmd(is_max?"maximize":"minimize"), + m_is_max(is_max) + {} + + virtual void reset(cmd_context & ctx) { } + + virtual char const * get_usage() const { return ""; } + virtual char const * get_descr(cmd_context & ctx) const { return "check sat modulo objective function";} + virtual unsigned get_arity() const { return 1; } + + virtual void prepare(cmd_context & ctx) {} + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR; } + + virtual void set_next_arg(cmd_context & ctx, expr * t) { + if (!is_app(t)) { + throw cmd_exception("malformed objective term: it cannot be a quantifier or bound variable"); + } + get_opt(ctx).add_objective(to_app(t), m_is_max); + } + + virtual void failure_cleanup(cmd_context & ctx) { + reset(ctx); + } + + virtual void execute(cmd_context & ctx) { + } +}; + + + +void install_opt_cmds(cmd_context & ctx) { + ctx.insert(alloc(assert_soft_cmd)); + ctx.insert(alloc(min_maximize_cmd, true)); + ctx.insert(alloc(min_maximize_cmd, false)); +} + + +#if 0 + ctx.insert(alloc(optimize_cmd)); + ctx.insert(alloc(assert_weighted_cmd)); + + +class optimize_cmd : public parametric_cmd { +public: + optimize_cmd(): + parametric_cmd("optimize") + {} + + virtual ~optimize_cmd() { + } + + virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + insert_timeout(p); + insert_max_memory(p); + p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); + opt::context::collect_param_descrs(p); + } + + virtual char const * get_main_descr() const { return "check sat modulo objective function";} + virtual char const * get_usage() const { return "( )*"; } + virtual void prepare(cmd_context & ctx) { + parametric_cmd::prepare(ctx); + } + virtual void failure_cleanup(cmd_context & ctx) { + reset(ctx); + } + + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + return parametric_cmd::next_arg_kind(ctx); + } + + + virtual void execute(cmd_context & ctx) { + params_ref p = ctx.params().merge_default_params(ps()); + opt::context& opt = get_opt(ctx); + opt.updt_params(p); + unsigned timeout = p.get_uint("timeout", UINT_MAX); + + ptr_vector::const_iterator it = ctx.begin_assertions(); + ptr_vector::const_iterator end = ctx.end_assertions(); + for (; it != end; ++it) { + opt.add_hard_constraint(*it); + } + lbool r = l_undef; + cancel_eh eh(opt); + { + scoped_ctrl_c ctrlc(eh); + scoped_timer timer(timeout, &eh); + cmd_context::scoped_watch sw(ctx); + try { + r = opt.optimize(); + if (r == l_true && opt.is_pareto()) { + while (r == l_true) { + display_result(ctx); + ctx.regular_stream() << "\n"; + r = opt.optimize(); + } + if (p.get_bool("print_statistics", false)) { + display_statistics(ctx); + } + return; + } + } + catch (z3_error& ex) { + ctx.regular_stream() << "(error: " << ex.msg() << "\")" << std::endl; + } + catch (z3_exception& ex) { + ctx.regular_stream() << "(error: " << ex.msg() << "\")" << std::endl; + } + } + switch(r) { + case l_true: { + ctx.regular_stream() << "sat\n"; + display_result(ctx); + break; + } + case l_false: + ctx.regular_stream() << "unsat\n"; + break; + case l_undef: + ctx.regular_stream() << "unknown\n"; + display_result(ctx); + break; + } + if (p.get_bool("print_statistics", false)) { + display_statistics(ctx); + } + } + + void display_result(cmd_context & ctx) { + params_ref p = ctx.params().merge_default_params(ps()); + opt::context& opt = get_opt(ctx); + opt.display_assignment(ctx.regular_stream()); + opt_params optp(p); + if (optp.print_model()) { + model_ref mdl; + opt.get_model(mdl); + if (mdl) { + ctx.regular_stream() << "(model " << std::endl; + model_smt2_pp(ctx.regular_stream(), ctx, *(mdl.get()), 2); + ctx.regular_stream() << ")" << std::endl; + } + } + } +private: + + void display_statistics(cmd_context& ctx) { + statistics stats; + get_opt(ctx).collect_statistics(stats); + stats.update("time", ctx.get_seconds()); + stats.display_smt2(ctx.regular_stream()); + } +}; + +class assert_weighted_cmd : public cmd { + unsigned m_idx; + expr* m_formula; + rational m_weight; + symbol m_id; + +public: + assert_weighted_cmd(): + cmd("assert-weighted"), + m_idx(0), + m_formula(0), + m_weight(0) + {} + + virtual ~assert_weighted_cmd() { + } + + virtual void reset(cmd_context & ctx) { + m_idx = 0; + m_formula = 0; + m_id = symbol::null; + } + + virtual char const * get_usage() const { return " "; } + virtual char const * get_descr(cmd_context & ctx) const { return "assert soft constraint with weight"; } + virtual unsigned get_arity() const { return VAR_ARITY; } + + // command invocation + virtual void prepare(cmd_context & ctx) {} + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + switch(m_idx) { + case 0: return CPK_EXPR; + case 1: return CPK_NUMERAL; + default: return CPK_SYMBOL; + } + } + virtual void set_next_arg(cmd_context & ctx, rational const & val) { + SASSERT(m_idx == 1); + if (!val.is_pos()) { + throw cmd_exception("Invalid weight. Weights must be positive."); + } + m_weight = val; + ++m_idx; + } + + virtual void set_next_arg(cmd_context & ctx, expr * t) { + SASSERT(m_idx == 0); + if (!ctx.m().is_bool(t)) { + throw cmd_exception("Invalid type for expression. Expected Boolean type."); + } + m_formula = t; + ++m_idx; + } + + virtual void set_next_arg(cmd_context & ctx, symbol const& s) { + SASSERT(m_idx > 1); + m_id = s; + ++m_idx; + } + + virtual void failure_cleanup(cmd_context & ctx) { + reset(ctx); + } + + virtual void execute(cmd_context & ctx) { + get_opt(ctx).add_soft_constraint(m_formula, m_weight, m_id); + reset(ctx); + } + + virtual void finalize(cmd_context & ctx) { + } + +}; + +#endif diff --git a/src/opt/opt_cmds.h b/src/opt/opt_cmds.h new file mode 100644 index 000000000..d8dd0dbfa --- /dev/null +++ b/src/opt/opt_cmds.h @@ -0,0 +1,28 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + opt_cmds.h + +Abstract: + Commands for optimization benchmarks + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-14 + +Notes: + +--*/ +#ifndef OPT_CMDS_H_ +#define OPT_CMDS_H_ + +#include "ast.h" + +class cmd_context; + +void install_opt_cmds(cmd_context & ctx); + + +#endif diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp new file mode 100644 index 000000000..dda38e566 --- /dev/null +++ b/src/opt/opt_context.cpp @@ -0,0 +1,1393 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + opt_context.cpp + +Abstract: + + Facility for running optimization problem. + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-16 + +Notes: + +--*/ + +#include "opt_context.h" +#include "ast_pp.h" +#include "opt_solver.h" +#include "opt_params.hpp" +#include "for_each_expr.h" +#include "goal.h" +#include "tactic.h" +#include "lia2card_tactic.h" +#include "elim01_tactic.h" +#include "solve_eqs_tactic.h" +#include "simplify_tactic.h" +#include "propagate_values_tactic.h" +#include "solve_eqs_tactic.h" +#include "elim_uncnstr_tactic.h" +#include "tactical.h" +#include "model_smt2_pp.h" +#include "card2bv_tactic.h" +#include "eq2bv_tactic.h" +#include "inc_sat_solver.h" +#include "bv_decl_plugin.h" +#include "pb_decl_plugin.h" +#include "ast_smt_pp.h" +#include "filter_model_converter.h" +#include "ast_pp_util.h" +#include "inc_sat_solver.h" + +namespace opt { + + void context::scoped_state::push() { + m_hard_lim.push_back(m_hard.size()); + m_objectives_lim.push_back(m_objectives.size()); + m_objectives_term_trail_lim.push_back(m_objectives_term_trail.size()); + } + + void context::scoped_state::pop() { + m_hard.resize(m_hard_lim.back()); + unsigned k = m_objectives_term_trail_lim.back(); + while (m_objectives_term_trail.size() > k) { + unsigned idx = m_objectives_term_trail.back(); + m_objectives[idx].m_terms.pop_back(); + m_objectives[idx].m_weights.pop_back(); + m_objectives_term_trail.pop_back(); + } + m_objectives_term_trail_lim.pop_back(); + k = m_objectives_lim.back(); + while (m_objectives.size() > k) { + objective& obj = m_objectives.back(); + if (obj.m_type == O_MAXSMT) { + m_indices.erase(obj.m_id); + } + m_objectives.pop_back(); + } + m_objectives_lim.pop_back(); + m_hard_lim.pop_back(); + } + + void context::scoped_state::add(expr* hard) { + m_hard.push_back(hard); + } + + bool context::scoped_state::set(ptr_vector & hard) { + bool eq = hard.size() == m_hard.size(); + for (unsigned i = 0; eq && i < hard.size(); ++i) { + eq = hard[i] == m_hard[i].get(); + } + m_hard.reset(); + m_hard.append(hard.size(), hard.c_ptr()); + return !eq; + } + + unsigned context::scoped_state::add(expr* f, rational const& w, symbol const& id) { + if (w.is_neg()) { + throw default_exception("Negative weight supplied. Weight should be positive"); + } + if (w.is_zero()) { + throw default_exception("Zero weight supplied. Weight should be positive"); + } + if (!m.is_bool(f)) { + throw default_exception("Soft constraint should be Boolean"); + } + if (!m_indices.contains(id)) { + m_objectives.push_back(objective(m, id)); + m_indices.insert(id, m_objectives.size() - 1); + } + SASSERT(m_indices.contains(id)); + unsigned idx = m_indices[id]; + m_objectives[idx].m_terms.push_back(f); + m_objectives[idx].m_weights.push_back(w); + m_objectives_term_trail.push_back(idx); + return idx; + } + + unsigned context::scoped_state::add(app* t, bool is_max) { + app_ref tr(t, m); + if (!m_bv.is_bv(t) && !m_arith.is_int_real(t)) { + throw default_exception("Objective must be bit-vector, integer or real"); + } + unsigned index = m_objectives.size(); + m_objectives.push_back(objective(is_max, tr, index)); + return index; + } + + context::context(ast_manager& m): + m(m), + m_arith(m), + m_bv(m), + m_hard_constraints(m), + m_solver(0), + m_box_index(UINT_MAX), + m_optsmt(m), + m_scoped_state(m), + m_fm(m), + m_objective_refs(m), + m_enable_sat(false), + m_is_clausal(false), + m_pp_neat(false) + { + params_ref p; + p.set_bool("model", true); + p.set_bool("unsat_core", true); + updt_params(p); + } + + context::~context() { + reset_maxsmts(); + } + + void context::reset_maxsmts() { + map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + m_maxsmts.reset(); + } + + void context::push() { + m_scoped_state.push(); + } + + void context::pop(unsigned n) { + for (unsigned i = 0; i < n; ++i) { + m_scoped_state.pop(); + } + clear_state(); + reset_maxsmts(); + m_optsmt.reset(); + m_hard_constraints.reset(); + } + + void context::set_hard_constraints(ptr_vector& fmls) { + if (m_scoped_state.set(fmls)) { + clear_state(); + } + } + + void context::add_hard_constraint(expr* f) { + m_scoped_state.add(f); + clear_state(); + } + + unsigned context::add_soft_constraint(expr* f, rational const& w, symbol const& id) { + clear_state(); + return m_scoped_state.add(f, w, id); + } + + unsigned context::add_objective(app* t, bool is_max) { + clear_state(); + return m_scoped_state.add(t, is_max); + } + + void context::import_scoped_state() { + m_optsmt.reset(); + reset_maxsmts(); + m_objectives.reset(); + m_hard_constraints.reset(); + scoped_state& s = m_scoped_state; + for (unsigned i = 0; i < s.m_objectives.size(); ++i) { + objective& obj = s.m_objectives[i]; + m_objectives.push_back(obj); + if (obj.m_type == O_MAXSMT) { + add_maxsmt(obj.m_id); + } + } + m_hard_constraints.append(s.m_hard); + } + + lbool context::optimize() { + if (m_pareto) { + return execute_pareto(); + } + if (m_box_index != UINT_MAX) { + return execute_box(); + } + clear_state(); + init_solver(); + import_scoped_state(); + normalize(); + internalize(); + update_solver(); + solver& s = get_solver(); + for (unsigned i = 0; i < m_hard_constraints.size(); ++i) { + TRACE("opt", tout << "Hard constraint: " << mk_ismt2_pp(m_hard_constraints[i].get(), m) << std::endl;); + s.assert_expr(m_hard_constraints[i].get()); + } + display_benchmark(); + IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n";); + lbool is_sat = s.check_sat(0,0); + TRACE("opt", tout << "initial search result: " << is_sat << "\n";); + if (is_sat != l_false) { + s.get_model(m_model); + } + if (is_sat != l_true) { + return is_sat; + } + IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n";); + TRACE("opt", model_smt2_pp(tout, m, *m_model, 0);); + m_optsmt.setup(*m_opt_solver.get()); + update_lower(); + switch (m_objectives.size()) { + case 0: + return is_sat; + case 1: + return execute(m_objectives[0], true, false); + default: { + opt_params optp(m_params); + symbol pri = optp.priority(); + if (pri == symbol("pareto")) { + return execute_pareto(); + } + else if (pri == symbol("box")) { + return execute_box(); + } + else { + return execute_lex(); + } + } + } + } + + + bool context::print_model() const { + opt_params optp(m_params); + return optp.print_model(); + } + + void context::get_base_model(model_ref& mdl) { + mdl = m_model; + } + + void context::fix_model(model_ref& mdl) { + if (mdl) { + if (m_model_converter) { + (*m_model_converter)(mdl, 0); + } + m_fm(mdl, 0); + } + } + + void context::set_model(model_ref& mdl) { + m_model = mdl; + fix_model(mdl); + } + + void context::get_model(model_ref& mdl) { + mdl = m_model; + fix_model(mdl); + } + + lbool context::execute_min_max(unsigned index, bool committed, bool scoped, bool is_max) { + if (scoped) get_solver().push(); + lbool result = m_optsmt.lex(index, is_max); + if (result == l_true) m_optsmt.get_model(m_model); + if (scoped) get_solver().pop(1); + if (result == l_true && committed) m_optsmt.commit_assignment(index); + return result; + } + + lbool context::execute_maxsat(symbol const& id, bool committed, bool scoped) { + model_ref tmp; + maxsmt& ms = *m_maxsmts.find(id); + if (scoped) get_solver().push(); + lbool result = ms(); + if (result != l_false && (ms.get_model(tmp), tmp.get())) ms.get_model(m_model); + if (scoped) get_solver().pop(1); + if (result == l_true && committed) ms.commit_assignment(); + return result; + } + + lbool context::execute(objective const& obj, bool committed, bool scoped) { + switch(obj.m_type) { + case O_MAXIMIZE: return execute_min_max(obj.m_index, committed, scoped, true); + case O_MINIMIZE: return execute_min_max(obj.m_index, committed, scoped, false); + case O_MAXSMT: return execute_maxsat(obj.m_id, committed, scoped); + default: UNREACHABLE(); return l_undef; + } + } + + /** + \brief there is no need to use push/pop when all objectives are maxsat and engine + is maxres. + */ + bool context::scoped_lex() { + if (m_maxsat_engine == symbol("maxres")) { + for (unsigned i = 0; i < m_objectives.size(); ++i) { + if (m_objectives[i].m_type != O_MAXSMT) return true; + } + return false; + } + return true; + } + + lbool context::execute_lex() { + lbool r = l_true; + bool sc = scoped_lex(); + IF_VERBOSE(1, verbose_stream() << "(optsmt:lex)\n";); + for (unsigned i = 0; r == l_true && i < m_objectives.size(); ++i) { + bool is_last = i + 1 == m_objectives.size(); + r = execute(m_objectives[i], i + 1 < m_objectives.size(), sc && !is_last); + if (r == l_true && !get_lower_as_num(i).is_finite()) { + return r; + } + if (r == l_true && i + 1 < m_objectives.size()) { + update_lower(); + } + } + DEBUG_CODE(if (r == l_true) validate_lex();); + return r; + } + + lbool context::execute_box() { + if (m_box_index < m_objectives.size()) { + m_model = m_box_models[m_box_index]; + ++m_box_index; + return l_true; + } + if (m_box_index != UINT_MAX && m_box_index >= m_objectives.size()) { + m_box_index = UINT_MAX; + return l_false; + } + m_box_index = 1; + m_box_models.reset(); + lbool r = m_optsmt.box(); + for (unsigned i = 0, j = 0; r == l_true && i < m_objectives.size(); ++i) { + objective const& obj = m_objectives[i]; + if (obj.m_type == O_MAXSMT) { + solver::scoped_push _sp(get_solver()); + r = execute(obj, false, false); + if (r == l_true) m_box_models.push_back(m_model.get()); + } + else { + m_box_models.push_back(m_optsmt.get_model(j)); + ++j; + } + } + if (r == l_true && m_objectives.size() > 0) { + m_model = m_box_models[0]; + } + return r; + } + + + expr_ref context::mk_le(unsigned i, model_ref& mdl) { + objective const& obj = m_objectives[i]; + return mk_cmp(false, mdl, obj); + } + + expr_ref context::mk_ge(unsigned i, model_ref& mdl) { + objective const& obj = m_objectives[i]; + return mk_cmp(true, mdl, obj); + } + + + expr_ref context::mk_gt(unsigned i, model_ref& mdl) { + expr_ref result = mk_le(i, mdl); + result = m.mk_not(result); + return result; + } + + expr_ref context::mk_cmp(bool is_ge, model_ref& mdl, objective const& obj) { + rational k(0); + expr_ref val(m), result(m); + switch (obj.m_type) { + case O_MINIMIZE: + is_ge = !is_ge; + case O_MAXIMIZE: + VERIFY(mdl->eval(obj.m_term, val) && is_numeral(val, k)); + if (is_ge) { + result = mk_ge(obj.m_term, val); + } + else { + result = mk_ge(val, obj.m_term); + } + break; + case O_MAXSMT: { + pb_util pb(m); + unsigned sz = obj.m_terms.size(); + ptr_vector terms; + vector coeffs; + for (unsigned i = 0; i < sz; ++i) { + terms.push_back(obj.m_terms[i]); + coeffs.push_back(obj.m_weights[i]); + VERIFY(mdl->eval(obj.m_terms[i], val)); + if (m.is_true(val)) { + k += obj.m_weights[i]; + } + } + if (is_ge) { + result = pb.mk_ge(sz, coeffs.c_ptr(), terms.c_ptr(), k); + } + else { + result = pb.mk_le(sz, coeffs.c_ptr(), terms.c_ptr(), k); + } + break; + } + } + TRACE("opt", + tout << (is_ge?">= ":"<= ") << k << "\n"; + display_objective(tout, obj); + tout << "\n"; + tout << result << "\n";); + return result; + } + + expr_ref context::mk_ge(expr* t, expr* s) { + expr_ref result(m); + if (m_bv.is_bv(t)) { + result = m_bv.mk_ule(s, t); + } + else { + result = m_arith.mk_ge(t, s); + } + return result; + } + + void context::yield() { + m_pareto->get_model(m_model); + update_bound(true); + update_bound(false); + } + + lbool context::execute_pareto() { + + if (!m_pareto) { + set_pareto(alloc(gia_pareto, m, *this, m_solver.get(), m_params)); + } + lbool is_sat = (*(m_pareto.get()))(); + if (is_sat != l_true) { + set_pareto(0); + } + if (is_sat == l_true) { + yield(); + } + return is_sat; + } + + std::string context::reason_unknown() const { + if (m_solver.get()) { + return m_solver->reason_unknown(); + } + return std::string("unknown"); + } + + void context::display_bounds(std::ostream& out, bounds_t const& b) const { + for (unsigned i = 0; i < m_objectives.size(); ++i) { + objective const& obj = m_objectives[i]; + display_objective(out, obj); + if (obj.m_type == O_MAXIMIZE) { + out << " |-> [" << b[i].first << ":" << b[i].second << "]\n"; + } + else { + out << " |-> [" << -b[i].second << ":" << -b[i].first << "]\n"; + } + } + } + + solver& context::get_solver() { + return *m_solver.get(); + } + + void context::init_solver() { + setup_arith_solver(); + #pragma omp critical (opt_context) + { + m_opt_solver = alloc(opt_solver, m, m_params, m_fm); + m_opt_solver->set_logic(m_logic); + m_solver = m_opt_solver.get(); + } + if (opt_params(m_params).priority() == symbol("pareto") || + (opt_params(m_params).priority() == symbol("lex") && m_objectives.size() > 1)) { + m_opt_solver->ensure_pb(); + } + } + + void context::setup_arith_solver() { + opt_params p(m_params); + if (p.optsmt_engine() == symbol("symba") || + p.optsmt_engine() == symbol("farkas")) { + std::stringstream strm; + strm << AS_OPTINF; + gparams::set("smt.arith.solver", strm.str().c_str()); + } + } + + void context::update_solver() { + if (!m_enable_sat || !probe_bv()) { + return; + } + if (m_maxsat_engine != symbol("maxres") && + m_maxsat_engine != symbol("pd-maxres") && + m_maxsat_engine != symbol("bcd2") && + m_maxsat_engine != symbol("sls")) { + return; + } + if (opt_params(m_params).priority() == symbol("pareto")) { + return; + } + m_params.set_bool("minimize_core_partial", true); // false); + m_params.set_bool("minimize_core", true); + m_sat_solver = mk_inc_sat_solver(m, m_params); + unsigned sz = get_solver().get_num_assertions(); + for (unsigned i = 0; i < sz; ++i) { + m_sat_solver->assert_expr(get_solver().get_assertion(i)); + } + #pragma omp critical (opt_context) + { + m_solver = m_sat_solver.get(); + } + } + + void context::enable_sls(bool force) { + if ((force || m_enable_sls) && m_sat_solver.get()) { + m_params.set_bool("optimize_model", true); + m_sat_solver->updt_params(m_params); + } + } + + struct context::is_bv { + struct found {}; + ast_manager& m; + pb_util pb; + bv_util bv; + is_bv(ast_manager& m): m(m), pb(m), bv(m) {} + void operator()(var *) { throw found(); } + void operator()(quantifier *) { throw found(); } + void operator()(app *n) { + family_id fid = n->get_family_id(); + if (fid != m.get_basic_family_id() && + fid != pb.get_family_id() && + fid != bv.get_family_id() && + !is_uninterp_const(n)) { + throw found(); + } + } + }; + + bool context::probe_bv() { + expr_fast_mark1 visited; + is_bv proc(m); + try { + for (unsigned i = 0; i < m_objectives.size(); ++i) { + objective & obj = m_objectives[i]; + if (obj.m_type != O_MAXSMT) return false; + maxsmt& ms = *m_maxsmts.find(obj.m_id); + for (unsigned j = 0; j < ms.size(); ++j) { + quick_for_each_expr(proc, visited, ms[j]); + } + } + unsigned sz = get_solver().get_num_assertions(); + for (unsigned i = 0; i < sz; i++) { + quick_for_each_expr(proc, visited, get_solver().get_assertion(i)); + } + for (unsigned i = 0; i < m_hard_constraints.size(); ++i) { + quick_for_each_expr(proc, visited, m_hard_constraints[i].get()); + } + } + catch (is_bv::found) { + return false; + } + return true; + } + + struct context::is_propositional_fn { + struct found {}; + ast_manager& m; + is_propositional_fn(ast_manager& m): m(m) {} + void operator()(var *) { throw found(); } + void operator()(quantifier *) { throw found(); } + void operator()(app *n) { + family_id fid = n->get_family_id(); + if (fid != m.get_basic_family_id() && + !is_uninterp_const(n)) { + throw found(); + } + } + }; + + bool context::is_propositional(expr* p) { + expr* np; + if (is_uninterp_const(p) || (m.is_not(p, np) && is_uninterp_const(np))) { + return true; + } + is_propositional_fn proc(m); + expr_fast_mark1 visited; + try { + quick_for_each_expr(proc, visited, p); + } + catch (is_propositional_fn::found) { + return false; + } + return true; + } + + + void context::add_maxsmt(symbol const& id) { + maxsmt* ms = alloc(maxsmt, *this); + ms->updt_params(m_params); + #pragma omp critical (opt_context) + { + m_maxsmts.insert(id, ms); + } + } + + bool context::is_numeral(expr* e, rational & n) const { + unsigned sz; + return m_arith.is_numeral(e, n) || m_bv.is_numeral(e, n, sz); + } + + void context::normalize() { + expr_ref_vector fmls(m); + to_fmls(fmls); + simplify_fmls(fmls); + from_fmls(fmls); + } + + void context::simplify_fmls(expr_ref_vector& fmls) { + if (m_is_clausal) { + return; + } + + goal_ref g(alloc(goal, m, true, false)); + for (unsigned i = 0; i < fmls.size(); ++i) { + g->assert_expr(fmls[i].get()); + } + tactic_ref tac0 = + and_then(mk_simplify_tactic(m), + mk_propagate_values_tactic(m), + mk_solve_eqs_tactic(m), + // NB: mk_elim_uncstr_tactic(m) is not sound with soft constraints + mk_simplify_tactic(m)); + opt_params optp(m_params); + tactic_ref tac2, tac3, tac4; + if (optp.elim_01()) { + tac2 = mk_elim01_tactic(m); + tac3 = mk_lia2card_tactic(m); + tac4 = mk_eq2bv_tactic(m); + params_ref lia_p; + lia_p.set_bool("compile_equality", optp.pb_compile_equality()); + tac3->updt_params(lia_p); + set_simplify(and_then(tac0.get(), tac2.get(), tac3.get(), tac4.get(), mk_simplify_tactic(m))); + } + else { + tactic_ref tac1 = + and_then(tac0.get(), + mk_simplify_tactic(m)); + set_simplify(tac1.get()); + } + proof_converter_ref pc; + expr_dependency_ref core(m); + goal_ref_buffer result; + (*m_simplify)(g, result, m_model_converter, pc, core); + SASSERT(result.size() == 1); + goal* r = result[0]; + fmls.reset(); + expr_ref tmp(m); + for (unsigned i = 0; i < r->size(); ++i) { + fmls.push_back(r->form(i)); + } + } + + bool context::is_maximize(expr* fml, app_ref& term, expr*& orig_term, unsigned& index) { + if (is_app(fml) && m_objective_fns.find(to_app(fml)->get_decl(), index) && + m_objectives[index].m_type == O_MAXIMIZE) { + term = to_app(to_app(fml)->get_arg(0)); + orig_term = m_objective_orig.find(to_app(fml)->get_decl()); + return true; + } + return false; + } + + bool context::is_minimize(expr* fml, app_ref& term, expr*& orig_term, unsigned& index) { + if (is_app(fml) && m_objective_fns.find(to_app(fml)->get_decl(), index) && + m_objectives[index].m_type == O_MINIMIZE) { + term = to_app(to_app(fml)->get_arg(0)); + orig_term = m_objective_orig.find(to_app(fml)->get_decl()); + return true; + } + return false; + } + + bool context::is_maxsat(expr* fml, expr_ref_vector& terms, + vector& weights, rational& offset, + bool& neg, symbol& id, unsigned& index) { + if (!is_app(fml)) return false; + neg = false; + app* a = to_app(fml); + if (m_objective_fns.find(a->get_decl(), index) && m_objectives[index].m_type == O_MAXSMT) { + for (unsigned i = 0; i < a->get_num_args(); ++i) { + expr* arg = a->get_arg(i); + if (m.is_true(arg)) { + // skip + } + else if (m.is_false(arg)) { + offset += m_objectives[index].m_weights[i]; + } + else { + terms.push_back(arg); + weights.push_back(m_objectives[index].m_weights[i]); + } + } + id = m_objectives[index].m_id; + return true; + } + app_ref term(m); + expr* orig_term; + offset = rational::zero(); + bool is_max = is_maximize(fml, term, orig_term, index); + bool is_min = !is_max && is_minimize(fml, term, orig_term, index); + if (is_min && get_pb_sum(term, terms, weights, offset)) { + TRACE("opt", tout << "try to convert minimization" << mk_pp(term, m) << "\n";); + // minimize 2*x + 3*y + // <=> + // (assert-soft (not x) 2) + // (assert-soft (not y) 3) + // + for (unsigned i = 0; i < weights.size(); ++i) { + if (weights[i].is_neg()) { + offset += weights[i]; + weights[i].neg(); + } + else { + terms[i] = m.mk_not(terms[i].get()); + } + } + TRACE("opt", + tout << "Convert minimization " << mk_pp(orig_term, m) << "\n"; + tout << "to maxsat: " << term << "\n"; + for (unsigned i = 0; i < weights.size(); ++i) { + tout << mk_pp(terms[i].get(), m) << ": " << weights[i] << "\n"; + } + tout << "offset: " << offset << "\n"; + ); + std::ostringstream out; + out << mk_pp(orig_term, m); + id = symbol(out.str().c_str()); + return true; + } + if (is_max && get_pb_sum(term, terms, weights, offset)) { + TRACE("opt", tout << "try to convert maximization" << mk_pp(term, m) << "\n";); + // maximize 2*x + 3*y - z + // <=> + // (assert-soft x 2) + // (assert-soft y 3) + // (assert-soft (not z) 1) + // offset := 6 + // maximize = offset - penalty + // + for (unsigned i = 0; i < weights.size(); ++i) { + if (weights[i].is_neg()) { + weights[i].neg(); + terms[i] = m.mk_not(terms[i].get()); + } + offset += weights[i]; + } + neg = true; + std::ostringstream out; + out << mk_pp(orig_term, m); + id = symbol(out.str().c_str()); + return true; + } + if ((is_max || is_min) && m_bv.is_bv(term)) { + offset.reset(); + unsigned bv_size = m_bv.get_bv_size(term); + expr_ref val(m); + val = m_bv.mk_numeral(is_max, 1); + for (unsigned i = 0; i < bv_size; ++i) { + rational w = power(rational(2),i); + weights.push_back(w); + terms.push_back(m.mk_eq(val, m_bv.mk_extract(i, i, term))); + if (is_max) { + offset += w; + } + } + neg = is_max; + std::ostringstream out; + out << mk_pp(orig_term, m); + id = symbol(out.str().c_str()); + return true; + } + return false; + } + + expr* context::mk_objective_fn(unsigned index, objective_t ty, unsigned sz, expr*const* args) { + ptr_vector domain; + for (unsigned i = 0; i < sz; ++i) { + domain.push_back(m.get_sort(args[i])); + } + char const* name = ""; + switch(ty) { + case O_MAXIMIZE: name = "maximize"; break; + case O_MINIMIZE: name = "minimize"; break; + case O_MAXSMT: name = "maxsat"; break; + default: break; + } + func_decl* f = m.mk_fresh_func_decl(name,"", domain.size(), domain.c_ptr(), m.mk_bool_sort()); + m_objective_fns.insert(f, index); + m_objective_refs.push_back(f); + if (sz > 0) { + m_objective_orig.insert(f, args[0]); + } + return m.mk_app(f, sz, args); + } + + expr* context::mk_maximize(unsigned index, app* t) { + expr* t_ = t; + return mk_objective_fn(index, O_MAXIMIZE, 1, &t_); + } + + expr* context::mk_minimize(unsigned index, app* t) { + expr* t_ = t; + return mk_objective_fn(index, O_MINIMIZE, 1, &t_); + } + + expr* context::mk_maxsat(unsigned index, unsigned num_fmls, expr* const* fmls) { + return mk_objective_fn(index, O_MAXSMT, num_fmls, fmls); + } + + void context::from_fmls(expr_ref_vector const& fmls) { + TRACE("opt", + for (unsigned i = 0; i < fmls.size(); ++i) { + tout << mk_pp(fmls[i], m) << "\n"; + }); + m_hard_constraints.reset(); + expr* orig_term; + for (unsigned i = 0; i < fmls.size(); ++i) { + expr* fml = fmls[i]; + app_ref tr(m); + expr_ref_vector terms(m); + vector weights; + rational offset(0); + unsigned index; + symbol id; + bool neg; + if (is_maxsat(fml, terms, weights, offset, neg, id, index)) { + objective& obj = m_objectives[index]; + if (obj.m_type != O_MAXSMT) { + // change from maximize/minimize. + obj.m_id = id; + obj.m_type = O_MAXSMT; + obj.m_weights.append(weights); + SASSERT(!m_maxsmts.contains(id)); + add_maxsmt(id); + } + mk_atomic(terms); + SASSERT(obj.m_id == id); + obj.m_terms.reset(); + obj.m_terms.append(terms); + obj.m_adjust_value.set_offset(offset); + obj.m_adjust_value.set_negate(neg); + m_maxsmts.find(id)->set_adjust_value(obj.m_adjust_value); + TRACE("opt", tout << "maxsat: " << id << " offset:" << offset << "\n";); + } + else if (is_maximize(fml, tr, orig_term, index)) { + purify(tr); + m_objectives[index].m_term = tr; + } + else if (is_minimize(fml, tr, orig_term, index)) { + purify(tr); + m_objectives[index].m_term = tr; + m_objectives[index].m_adjust_value.set_negate(true); + } + else { + m_hard_constraints.push_back(fml); + } + } + } + + void context::purify(app_ref& term) { + filter_model_converter_ref fm; + if (m_arith.is_add(term)) { + expr_ref_vector args(m); + unsigned sz = term->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + expr* arg = term->get_arg(i); + if (is_mul_const(arg)) { + args.push_back(arg); + } + else { + args.push_back(purify(fm, arg)); + } + } + term = m_arith.mk_add(args.size(), args.c_ptr()); + } + else if (m_arith.is_arith_expr(term) && !is_mul_const(term)) { + TRACE("opt", tout << "Purifying " << term << "\n";); + term = purify(fm, term); + } + if (fm) { + m_model_converter = concat(m_model_converter.get(), fm.get()); + } + } + + bool context::is_mul_const(expr* e) { + expr* e1, *e2; + return + is_uninterp_const(e) || + m_arith.is_numeral(e) || + (m_arith.is_mul(e, e1, e2) && m_arith.is_numeral(e1) && is_uninterp_const(e2)) || + (m_arith.is_mul(e, e2, e1) && m_arith.is_numeral(e1) && is_uninterp_const(e2)); + } + + app* context::purify(filter_model_converter_ref& fm, expr* term) { + std::ostringstream out; + out << mk_pp(term, m); + app* q = m.mk_fresh_const(out.str().c_str(), m.get_sort(term)); + if (!fm) fm = alloc(filter_model_converter, m); + m_hard_constraints.push_back(m.mk_eq(q, term)); + fm->insert(q->get_decl()); + return q; + } + + /** + To select the proper theory solver we have to ensure that all theory + symbols from soft constraints are reflected in the hard constraints. + + - filter "obj" from generated model. + */ + void context::mk_atomic(expr_ref_vector& terms) { + filter_model_converter_ref fm; + for (unsigned i = 0; i < terms.size(); ++i) { + expr_ref p(terms[i].get(), m); + app_ref q(m); + if (is_propositional(p)) { + terms[i] = p; + } + else { + terms[i] = purify(fm, p); + } + } + if (fm) { + m_model_converter = concat(m_model_converter.get(), fm.get()); + } + } + + void context::to_fmls(expr_ref_vector& fmls) { + m_objective_fns.reset(); + fmls.append(m_hard_constraints); + for (unsigned i = 0; i < m_objectives.size(); ++i) { + objective const& obj = m_objectives[i]; + switch(obj.m_type) { + case O_MINIMIZE: + fmls.push_back(mk_minimize(i, obj.m_term)); + break; + case O_MAXIMIZE: + fmls.push_back(mk_maximize(i, obj.m_term)); + break; + case O_MAXSMT: + fmls.push_back(mk_maxsat(i, obj.m_terms.size(), obj.m_terms.c_ptr())); + break; + } + } + TRACE("opt", + for (unsigned i = 0; i < fmls.size(); ++i) { + tout << mk_pp(fmls[i].get(), m) << "\n"; + }); + } + + void context::internalize() { + for (unsigned i = 0; i < m_objectives.size(); ++i) { + objective & obj = m_objectives[i]; + switch(obj.m_type) { + case O_MINIMIZE: { + app_ref tmp(m); + tmp = m_arith.mk_uminus(obj.m_term); + obj.m_index = m_optsmt.add(tmp); + break; + } + case O_MAXIMIZE: + obj.m_index = m_optsmt.add(obj.m_term); + break; + case O_MAXSMT: { + maxsmt& ms = *m_maxsmts.find(obj.m_id); + for (unsigned j = 0; j < obj.m_terms.size(); ++j) { + ms.add(obj.m_terms[j].get(), obj.m_weights[j]); + } + break; + } + } + } + } + + void context::update_bound(bool is_lower) { + expr_ref val(m); + if (!m_model.get()) return; + for (unsigned i = 0; i < m_objectives.size(); ++i) { + objective const& obj = m_objectives[i]; + rational r; + switch(obj.m_type) { + case O_MINIMIZE: { + bool evaluated = m_model->eval(obj.m_term, val); + TRACE("opt", tout << obj.m_term << " " << val << " " << evaluated << " " << is_numeral(val, r) << "\n";); + if (evaluated && is_numeral(val, r)) { + inf_eps val = inf_eps(obj.m_adjust_value(r)); + TRACE("opt", tout << "adjusted value: " << val << "\n";); + if (is_lower) { + m_optsmt.update_lower(obj.m_index, val); + } + else { + m_optsmt.update_upper(obj.m_index, val); + } + } + break; + } + case O_MAXIMIZE: { + bool evaluated = m_model->eval(obj.m_term, val); + TRACE("opt", tout << obj.m_term << " " << val << "\n";); + if (evaluated && is_numeral(val, r)) { + inf_eps val = inf_eps(obj.m_adjust_value(r)); + TRACE("opt", tout << "adjusted value: " << val << "\n";); + if (is_lower) { + m_optsmt.update_lower(obj.m_index, val); + } + else { + m_optsmt.update_upper(obj.m_index, val); + } + } + break; + } + case O_MAXSMT: { + bool ok = true; + for (unsigned j = 0; ok && j < obj.m_terms.size(); ++j) { + bool evaluated = m_model->eval(obj.m_terms[j], val); + TRACE("opt", tout << mk_pp(obj.m_terms[j], m) << " " << val << "\n";); + if (evaluated) { + if (!m.is_true(val)) { + r += obj.m_weights[j]; + } + } + else { + ok = false; + } + } + if (ok) { + maxsmt& ms = *m_maxsmts.find(obj.m_id); + if (is_lower) { + ms.update_upper(r); + TRACE("opt", tout << r << " " << ms.get_upper() << "\n";); + } + else { + ms.update_lower(r); + TRACE("opt", tout << r << " " << ms.get_lower() << "\n";); + } + } + break; + } + } + } + } + + void context::display_benchmark() { + if (opt_params(m_params).dump_benchmarks() && + sat_enabled() && + m_objectives.size() == 1 && + m_objectives[0].m_type == O_MAXSMT + ) { + objective& o = m_objectives[0]; + unsigned sz = o.m_terms.size(); + inc_sat_display(verbose_stream(), get_solver(), sz, o.m_terms.c_ptr(), o.m_weights.c_ptr()); + } + } + + void context::display(std::ostream& out) { + display_assignment(out); + } + + void context::display_assignment(std::ostream& out) { + for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { + objective const& obj = m_scoped_state.m_objectives[i]; + display_objective(out, obj); + if (get_lower_as_num(i) != get_upper_as_num(i)) { + out << " |-> [" << get_lower(i) << ":" << get_upper(i) << "]\n"; + } + else { + out << " |-> " << get_lower(i) << "\n"; + } + } + } + + void context::display_objective(std::ostream& out, objective const& obj) const { + switch(obj.m_type) { + case O_MAXSMT: { + symbol s = obj.m_id; + if (s != symbol::null) { + out << s; + } + break; + } + default: + out << obj.m_term; + break; + } + } + + inf_eps context::get_lower_as_num(unsigned idx) { + if (idx > m_objectives.size()) { + throw default_exception("index out of bounds"); + } + objective const& obj = m_objectives[idx]; + switch(obj.m_type) { + case O_MAXSMT: + return inf_eps(m_maxsmts.find(obj.m_id)->get_lower()); + case O_MINIMIZE: + return obj.m_adjust_value(m_optsmt.get_upper(obj.m_index)); + case O_MAXIMIZE: + return obj.m_adjust_value(m_optsmt.get_lower(obj.m_index)); + default: + UNREACHABLE(); + return inf_eps(); + } + } + + inf_eps context::get_upper_as_num(unsigned idx) { + if (idx > m_objectives.size()) { + throw default_exception("index out of bounds"); + } + objective const& obj = m_objectives[idx]; + switch(obj.m_type) { + case O_MAXSMT: + return inf_eps(m_maxsmts.find(obj.m_id)->get_upper()); + case O_MINIMIZE: + return obj.m_adjust_value(m_optsmt.get_lower(obj.m_index)); + case O_MAXIMIZE: + return obj.m_adjust_value(m_optsmt.get_upper(obj.m_index)); + default: + UNREACHABLE(); + return inf_eps(); + } + } + + expr_ref context::get_lower(unsigned idx) { + return to_expr(get_lower_as_num(idx)); + } + + expr_ref context::get_upper(unsigned idx) { + return to_expr(get_upper_as_num(idx)); + } + + expr_ref context::to_expr(inf_eps const& n) { + rational inf = n.get_infinity(); + rational r = n.get_rational(); + rational eps = n.get_infinitesimal(); + expr_ref_vector args(m); + if (!inf.is_zero()) { + expr* oo = m.mk_const(symbol("oo"), m_arith.mk_int()); + if (inf.is_one()) { + args.push_back(oo); + } + else { + args.push_back(m_arith.mk_mul(m_arith.mk_numeral(inf, inf.is_int()), oo)); + } + } + if (!r.is_zero()) { + args.push_back(m_arith.mk_numeral(r, r.is_int())); + } + if (!eps.is_zero()) { + expr* ep = m.mk_const(symbol("epsilon"), m_arith.mk_real()); + if (eps.is_one()) { + args.push_back(ep); + } + else { + args.push_back(m_arith.mk_mul(m_arith.mk_numeral(eps, eps.is_int()), ep)); + } + } + switch(args.size()) { + case 0: return expr_ref(m_arith.mk_numeral(rational(0), true), m); + case 1: return expr_ref(args[0].get(), m); + default: return expr_ref(m_arith.mk_add(args.size(), args.c_ptr()), m); + } + } + + void context::set_simplify(tactic* tac) { + #pragma omp critical (opt_context) + { + m_simplify = tac; + } + } + + void context::clear_state() { + set_pareto(0); + m_box_index = UINT_MAX; + m_model.reset(); + } + + void context::set_pareto(pareto_base* p) { + #pragma omp critical (opt_context) + { + m_pareto = p; + } + } + + void context::set_cancel(bool f) { + #pragma omp critical (opt_context) + { + if (m_solver) { + if (f) m_solver->cancel(); else m_solver->reset_cancel(); + } + if (m_pareto) { + m_pareto->set_cancel(f); + } + if (m_simplify) { + if (f) m_simplify->cancel(); else m_solver->reset_cancel(); + } + map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); + for (; it != end; ++it) { + it->m_value->set_cancel(f); + } + } + m_optsmt.set_cancel(f); + } + + void context::collect_statistics(statistics& stats) const { + if (m_solver) { + m_solver->collect_statistics(stats); + } + if (m_simplify) { + m_simplify->collect_statistics(stats); + } + map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); + for (; it != end; ++it) { + it->m_value->collect_statistics(stats); + } + get_memory_statistics(stats); + get_rlimit_statistics(m.limit(), stats); + } + + void context::collect_param_descrs(param_descrs & r) { + opt_params::collect_param_descrs(r); + } + + void context::updt_params(params_ref& p) { + m_params.append(p); + if (m_solver) { + m_solver->updt_params(m_params); + } + m_optsmt.updt_params(m_params); + map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); + for (; it != end; ++it) { + it->m_value->updt_params(m_params); + } + opt_params _p(p); + m_enable_sat = _p.enable_sat(); + m_enable_sls = _p.enable_sls(); + m_maxsat_engine = _p.maxsat_engine(); + m_pp_neat = _p.pp_neat(); + } + + std::string context::to_string() const { + smt2_pp_environment_dbg env(m); + ast_pp_util visitor(m); + std::ostringstream out; +#define PP(_e_) ast_smt2_pp(out, _e_, env); + visitor.collect(m_scoped_state.m_hard); + + for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { + objective const& obj = m_scoped_state.m_objectives[i]; + switch(obj.m_type) { + case O_MAXIMIZE: + case O_MINIMIZE: + visitor.collect(obj.m_term); + break; + case O_MAXSMT: + visitor.collect(obj.m_terms); + break; + default: + UNREACHABLE(); + break; + } + } + + visitor.display_decls(out); + visitor.display_asserts(out, m_scoped_state.m_hard, m_pp_neat); + for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { + objective const& obj = m_scoped_state.m_objectives[i]; + switch(obj.m_type) { + case O_MAXIMIZE: + out << "(maximize "; + PP(obj.m_term); + out << ")\n"; + break; + case O_MINIMIZE: + out << "(minimize "; + PP(obj.m_term); + out << ")\n"; + break; + case O_MAXSMT: + for (unsigned j = 0; j < obj.m_terms.size(); ++j) { + out << "(assert-soft "; + PP(obj.m_terms[j]); + rational w = obj.m_weights[j]; + if (w.is_int()) { + out << " :weight " << w; + } + else { + out << " :dweight " << w; + } + if (obj.m_id != symbol::null) { + out << " :id " << obj.m_id; + } + out << ")\n"; + } + break; + default: + UNREACHABLE(); + break; + } + } + + param_descrs descrs; + collect_param_descrs(descrs); + m_params.display_smt2(out, "opt", descrs); + + out << "(check-sat)\n"; + return out.str(); + } + + void context::validate_lex() { + rational r1; + expr_ref val(m); + for (unsigned i = 0; i < m_objectives.size(); ++i) { + objective const& obj = m_objectives[i]; + switch(obj.m_type) { + case O_MINIMIZE: + case O_MAXIMIZE: { + inf_eps n = m_optsmt.get_lower(obj.m_index); + if (m_optsmt.objective_is_model_valid(obj.m_index) && + n.get_infinity().is_zero() && + n.get_infinitesimal().is_zero() && + m_model->eval(obj.m_term, val) && + is_numeral(val, r1)) { + rational r2 = n.get_rational(); + if (obj.m_type == O_MINIMIZE) { + r1.neg(); + } + CTRACE("opt", r1 != r2, tout << obj.m_term << " evaluates to " << r1 << " but has objective " << r2 << "\n";); + CTRACE("opt", r1 != r2, model_smt2_pp(tout, m, *m_model, 0);); + SASSERT(r1 == r2); + } + break; + } + case O_MAXSMT: { + rational value(0); + for (unsigned i = 0; i < obj.m_terms.size(); ++i) { + VERIFY(m_model->eval(obj.m_terms[i], val)); + if (!m.is_true(val)) { + value += obj.m_weights[i]; + } + // TBD: check that optimal was not changed. + } + TRACE("opt", tout << "value " << value << "\n";); + break; + } + } + } + } +} diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h new file mode 100644 index 000000000..735dc6905 --- /dev/null +++ b/src/opt/opt_context.h @@ -0,0 +1,297 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + opt_context.h + +Abstract: + Facility for running optimization problem. + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-16 + +Notes: + +--*/ +#ifndef OPT_CONTEXT_H_ +#define OPT_CONTEXT_H_ + +#include "ast.h" +#include "opt_solver.h" +#include "opt_pareto.h" +#include "optsmt.h" +#include "maxsmt.h" +#include "model_converter.h" +#include "tactic.h" +#include "arith_decl_plugin.h" +#include "bv_decl_plugin.h" +#include "cmd_context.h" + + +namespace opt { + + class opt_solver; + + /** + \brief base class required by MaxSMT solvers. + By implementing a base class, you can invoke the MaxSMT solvers + independent of the overall optimization infrastructure. + The caller has to supply a solver object that encapsulates + an incremental SAT or SMT solver. The MaxSMT solvers may assume that + the solver object should be in a satisfiable state and contain an initial model. + */ + + class maxsat_context { + public: + virtual filter_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. + virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled? + virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver) + virtual ast_manager& get_manager() = 0; + virtual params_ref& params() = 0; + virtual void enable_sls(bool force) = 0; // stochastic local search + virtual symbol const& maxsat_engine() const = 0; // retrieve maxsat engine configuration parameter. + virtual void get_base_model(model_ref& _m) = 0; // retrieve model from initial satisfiability call. + virtual smt::context& smt_context() = 0; // access SMT context for SMT based MaxSMT solver (wmax requires SMT core) + virtual unsigned num_objectives() = 0; + }; + + /** + \brief main context object for optimization. + Hard and soft assertions, and objectives are registered with this context. + It handles combinations of objectives. + */ + + class context : + public opt_wrapper, + public pareto_callback, + public maxsat_context { + typedef map map_t; + typedef map map_id; + typedef vector > bounds_t; + enum objective_t { + O_MAXIMIZE, + O_MINIMIZE, + O_MAXSMT + }; + + struct objective { + objective_t m_type; + app_ref m_term; // for maximize, minimize term + expr_ref_vector m_terms; // for maxsmt + vector m_weights; // for maxsmt + adjust_value m_adjust_value; + symbol m_id; // for maxsmt + unsigned m_index; // for maximize/minimize index + + objective(bool is_max, app_ref& t, unsigned idx): + m_type(is_max?O_MAXIMIZE:O_MINIMIZE), + m_term(t), + m_terms(t.get_manager()), + m_id(), + m_index(idx) + { + if (!is_max) { + m_adjust_value.set_negate(true); + } + } + + objective(ast_manager& m, symbol id): + m_type(O_MAXSMT), + m_term(m), + m_terms(m), + m_id(id), + m_index(0) + {} + }; + + class scoped_state { + ast_manager& m; + arith_util m_arith; + bv_util m_bv; + unsigned_vector m_hard_lim; + unsigned_vector m_objectives_lim; + unsigned_vector m_objectives_term_trail; + unsigned_vector m_objectives_term_trail_lim; + map_id m_indices; + + public: + expr_ref_vector m_hard; + vector m_objectives; + + scoped_state(ast_manager& m): + m(m), + m_arith(m), + m_bv(m), + m_hard(m) + {} + void push(); + void pop(); + void add(expr* hard); + bool set(ptr_vector & hard); + unsigned add(expr* soft, rational const& weight, symbol const& id); + unsigned add(app* obj, bool is_max); + }; + + ast_manager& m; + arith_util m_arith; + bv_util m_bv; + expr_ref_vector m_hard_constraints; + ref m_opt_solver; + ref m_solver; + ref m_sat_solver; + scoped_ptr m_pareto; + sref_vector m_box_models; + unsigned m_box_index; + params_ref m_params; + optsmt m_optsmt; + map_t m_maxsmts; + scoped_state m_scoped_state; + vector m_objectives; + model_ref m_model; + model_converter_ref m_model_converter; + filter_model_converter m_fm; + obj_map m_objective_fns; + obj_map m_objective_orig; + func_decl_ref_vector m_objective_refs; + tactic_ref m_simplify; + bool m_enable_sat; + bool m_enable_sls; + bool m_is_clausal; + bool m_pp_neat; + symbol m_maxsat_engine; + symbol m_logic; + public: + context(ast_manager& m); + virtual ~context(); + unsigned add_soft_constraint(expr* f, rational const& w, symbol const& id); + unsigned add_objective(app* t, bool is_max); + void add_hard_constraint(expr* f); + + + virtual void push(); + virtual void pop(unsigned n); + virtual bool empty() { return m_scoped_state.m_objectives.empty(); } + virtual void set_cancel(bool f); + virtual void reset_cancel() { set_cancel(false); } + virtual void cancel() { set_cancel(true); } + virtual void set_hard_constraints(ptr_vector & hard); + virtual lbool optimize(); + virtual bool print_model() const; + virtual void get_model(model_ref& _m); + virtual void set_model(model_ref& _m); + virtual void fix_model(model_ref& _m); + virtual void collect_statistics(statistics& stats) const; + virtual proof* get_proof() { return 0; } + virtual void get_labels(svector & r) {} + virtual void get_unsat_core(ptr_vector & r) {} + virtual std::string reason_unknown() const; + + virtual void display_assignment(std::ostream& out); + virtual bool is_pareto() { return m_pareto.get() != 0; } + virtual void set_logic(symbol const& s) { m_logic = s; } + void set_clausal(bool f) { m_is_clausal = f; } + + void display(std::ostream& out); + static void collect_param_descrs(param_descrs & r); + void updt_params(params_ref& p); + params_ref& get_params() { return m_params; } + + expr_ref get_lower(unsigned idx); + expr_ref get_upper(unsigned idx); + + std::string to_string() const; + + + virtual unsigned num_objectives() { return m_objectives.size(); } + virtual expr_ref mk_gt(unsigned i, model_ref& model); + virtual expr_ref mk_ge(unsigned i, model_ref& model); + virtual expr_ref mk_le(unsigned i, model_ref& model); + + virtual smt::context& smt_context() { return m_opt_solver->get_context(); } + virtual filter_model_converter& fm() { return m_fm; } + virtual bool sat_enabled() const { return 0 != m_sat_solver.get(); } + virtual solver& get_solver(); + virtual ast_manager& get_manager() { return this->m; } + virtual params_ref& params() { return m_params; } + virtual void enable_sls(bool force); + virtual symbol const& maxsat_engine() const { return m_maxsat_engine; } + virtual void get_base_model(model_ref& _m); + + + private: + void validate_feasibility(maxsmt& ms); + + lbool execute(objective const& obj, bool committed, bool scoped); + lbool execute_min_max(unsigned index, bool committed, bool scoped, bool is_max); + lbool execute_maxsat(symbol const& s, bool committed, bool scoped); + lbool execute_lex(); + lbool execute_box(); + lbool execute_pareto(); + bool scoped_lex(); + expr_ref to_expr(inf_eps const& n); + + void reset_maxsmts(); + void import_scoped_state(); + void normalize(); + void internalize(); + bool is_maximize(expr* fml, app_ref& term, expr*& orig_term, unsigned& index); + bool is_minimize(expr* fml, app_ref& term, expr*& orig_term, unsigned& index); + bool is_maxsat(expr* fml, expr_ref_vector& terms, + vector& weights, rational& offset, bool& neg, + symbol& id, unsigned& index); + void purify(app_ref& term); + app* purify(filter_model_converter_ref& fm, expr* e); + bool is_mul_const(expr* e); + expr* mk_maximize(unsigned index, app* t); + expr* mk_minimize(unsigned index, app* t); + expr* mk_maxsat(unsigned index, unsigned num_fmls, expr* const* fmls); + expr* mk_objective_fn(unsigned index, objective_t ty, unsigned sz, expr*const* args); + void to_fmls(expr_ref_vector& fmls); + void from_fmls(expr_ref_vector const& fmls); + void simplify_fmls(expr_ref_vector& fmls); + void mk_atomic(expr_ref_vector& terms); + + void update_lower() { update_bound(true); } + void update_bound(bool is_lower); + + inf_eps get_lower_as_num(unsigned idx); + inf_eps get_upper_as_num(unsigned idx); + + + struct is_bv; + bool probe_bv(); + + struct is_propositional_fn; + bool is_propositional(expr* e); + + void init_solver(); + void update_solver(); + void setup_arith_solver(); + void add_maxsmt(symbol const& id); + void set_simplify(tactic *simplify); + void set_pareto(pareto_base* p); + void clear_state(); + + bool is_numeral(expr* e, rational& n) const; + + void display_objective(std::ostream& out, objective const& obj) const; + void display_bounds(std::ostream& out, bounds_t const& b) const; + + + void validate_lex(); + + void display_benchmark(); + + + // pareto + void yield(); + expr_ref mk_ge(expr* t, expr* s); + expr_ref mk_cmp(bool is_ge, model_ref& mdl, objective const& obj); + + }; + +} + +#endif diff --git a/src/opt/opt_params.pyg b/src/opt/opt_params.pyg new file mode 100644 index 000000000..37b9ff372 --- /dev/null +++ b/src/opt/opt_params.pyg @@ -0,0 +1,29 @@ +def_module_params('opt', + description='optimization parameters', + export=True, + params=(('timeout', UINT, UINT_MAX, 'set timeout'), + ('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'farkas', 'symba'"), + ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'fu_malik', 'core_maxsat', 'wmax', 'pbmax', 'maxres', 'pd-maxres', 'bcd2', 'wpm2', 'sls', 'maxhs'"), + ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', or 'box'"), + ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), + ('print_model', BOOL, False, 'display model for satisfiable constraints'), + ('enable_sls', BOOL, False, 'enable SLS tuning during weighted maxsast'), + ('enable_sat', BOOL, True, 'enable the new SAT core for propositional constraints'), + ('elim_01', BOOL, True, 'eliminate 01 variables'), + ('pp.neat', BOOL, True, 'use neat (as opposed to less readable, but faster) pretty printer when displaying context'), + ('pb.compile_equality', BOOL, False, 'compile arithmetical equalities into pseudo-Boolean equality (instead of two inequalites)'), + ('maxres.hill_climb', BOOL, True, 'give preference for large weight cores'), + ('maxres.add_upper_bound_block', BOOL, False, 'restict upper bound with constraint'), + ('maxres.max_num_cores', UINT, UINT_MAX, 'maximal number of cores per round'), + ('maxres.max_core_size', UINT, 3, 'break batch of generated cores if size reaches this number'), + ('maxres.maximize_assignment', BOOL, False, 'find an MSS/MCS to improve current assignment'), + ('maxres.max_correction_set_size', UINT, 3, 'allow generating correction set constraints up to maximal size'), + ('maxres.wmax', BOOL, False, 'use weighted theory solver to constrain upper bounds'), + ('maxres.pivot_on_correction_set', BOOL, True, 'reduce soft constraints if the current correction set is smaller than current core') + + )) + + + + + diff --git a/src/opt/opt_pareto.cpp b/src/opt/opt_pareto.cpp new file mode 100644 index 000000000..5e1d4b269 --- /dev/null +++ b/src/opt/opt_pareto.cpp @@ -0,0 +1,105 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + opt_pareto.cpp + +Abstract: + + Pareto front utilities + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-24 + +Notes: + + +--*/ + +#include "opt_pareto.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" + +namespace opt { + + // --------------------- + // GIA pareto algorithm + + lbool gia_pareto::operator()() { + expr_ref fml(m); + lbool is_sat = m_solver->check_sat(0, 0); + if (is_sat == l_true) { + { + solver::scoped_push _s(*m_solver.get()); + while (is_sat == l_true) { + if (m_cancel) { + return l_undef; + } + m_solver->get_model(m_model); + IF_VERBOSE(1, + model_ref mdl(m_model); + cb.fix_model(mdl); + model_smt2_pp(verbose_stream() << "new model:\n", m, *mdl, 0);); + // TBD: we can also use local search to tune solution coordinate-wise. + mk_dominates(); + is_sat = m_solver->check_sat(0, 0); + } + } + if (is_sat == l_undef) { + return l_undef; + } + SASSERT(is_sat == l_false); + is_sat = l_true; + mk_not_dominated_by(); + } + return is_sat; + } + + void pareto_base::mk_dominates() { + unsigned sz = cb.num_objectives(); + expr_ref fml(m); + expr_ref_vector gt(m), fmls(m); + for (unsigned i = 0; i < sz; ++i) { + fmls.push_back(cb.mk_ge(i, m_model)); + gt.push_back(cb.mk_gt(i, m_model)); + } + fmls.push_back(m.mk_or(gt.size(), gt.c_ptr())); + fml = m.mk_and(fmls.size(), fmls.c_ptr()); + IF_VERBOSE(10, verbose_stream() << "dominates: " << fml << "\n";); + TRACE("opt", tout << fml << "\n"; model_smt2_pp(tout, m, *m_model, 0);); + m_solver->assert_expr(fml); + } + + void pareto_base::mk_not_dominated_by() { + unsigned sz = cb.num_objectives(); + expr_ref fml(m); + expr_ref_vector le(m); + for (unsigned i = 0; i < sz; ++i) { + le.push_back(cb.mk_le(i, m_model)); + } + fml = m.mk_not(m.mk_and(le.size(), le.c_ptr())); + IF_VERBOSE(10, verbose_stream() << "not dominated by: " << fml << "\n";); + TRACE("opt", tout << fml << "\n";); + m_solver->assert_expr(fml); + } + + // --------------------------------- + // OIA algorithm (without filtering) + + lbool oia_pareto::operator()() { + solver::scoped_push _s(*m_solver.get()); + lbool is_sat = m_solver->check_sat(0, 0); + if (m_cancel) { + is_sat = l_undef; + } + if (is_sat == l_true) { + m_solver->get_model(m_model); + mk_not_dominated_by(); + } + return is_sat; + } + +} + diff --git a/src/opt/opt_pareto.h b/src/opt/opt_pareto.h new file mode 100644 index 000000000..88a3ce9c1 --- /dev/null +++ b/src/opt/opt_pareto.h @@ -0,0 +1,118 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + opt_pareto.h + +Abstract: + + Pareto front utilities + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-24 + +Notes: + + +--*/ +#ifndef OPT_PARETO_H_ +#define OPT_PARETO_H_ + +#include "solver.h" +#include "model.h" + +namespace opt { + + class pareto_callback { + public: + virtual unsigned num_objectives() = 0; + virtual expr_ref mk_gt(unsigned i, model_ref& model) = 0; + virtual expr_ref mk_ge(unsigned i, model_ref& model) = 0; + virtual expr_ref mk_le(unsigned i, model_ref& model) = 0; + virtual void set_model(model_ref& m) = 0; + virtual void fix_model(model_ref& m) = 0; + }; + class pareto_base { + protected: + ast_manager& m; + pareto_callback& cb; + volatile bool m_cancel; + ref m_solver; + params_ref m_params; + model_ref m_model; + public: + pareto_base( + ast_manager & m, + pareto_callback& cb, + solver* s, + params_ref & p): + m(m), + cb(cb), + m_cancel(false), + m_solver(s), + m_params(p) { + } + virtual ~pareto_base() {} + virtual void updt_params(params_ref & p) { + m_solver->updt_params(p); + m_params.copy(p); + } + virtual void collect_param_descrs(param_descrs & r) { + m_solver->collect_param_descrs(r); + } + virtual void collect_statistics(statistics & st) const { + m_solver->collect_statistics(st); + } + virtual void set_cancel(bool f) { + if (f) + m_solver->cancel(); + else + m_solver->reset_cancel(); + m_cancel = f; + } + virtual void display(std::ostream & out) const { + m_solver->display(out); + } + virtual lbool operator()() = 0; + + virtual void get_model(model_ref& mdl) { + mdl = m_model; + } + + protected: + + void mk_dominates(); + + void mk_not_dominated_by(); + }; + class gia_pareto : public pareto_base { + public: + gia_pareto(ast_manager & m, + pareto_callback& cb, + solver* s, + params_ref & p): + pareto_base(m, cb, s, p) { + } + virtual ~gia_pareto() {} + + virtual lbool operator()(); + }; + + // opportunistic improvement algorithm. + class oia_pareto : public pareto_base { + public: + oia_pareto(ast_manager & m, + pareto_callback& cb, + solver* s, + params_ref & p): + pareto_base(m, cb, s, p) { + } + virtual ~oia_pareto() {} + + virtual lbool operator()(); + }; +} + +#endif diff --git a/src/opt/opt_sls_solver.h b/src/opt/opt_sls_solver.h new file mode 100644 index 000000000..5071563f7 --- /dev/null +++ b/src/opt/opt_sls_solver.h @@ -0,0 +1,251 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + opt_sls_solver.h + +Abstract: + + Wraps a solver with SLS for improving a solution using an objective function. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-18 + +Notes: + + +--*/ +#ifndef OPT_SLS_SOLVER_H_ +#define OPT_SLS_SOLVER_H_ + +#include "solver_na2as.h" +#include "card2bv_tactic.h" +#include "nnf_tactic.h" +#include "pb_sls.h" +#include "bvsls_opt_engine.h" + + +namespace opt { + + class sls_solver : public solver_na2as { + ast_manager& m; + ref m_solver; + scoped_ptr m_bvsls; + scoped_ptr m_pbsls; + pb::card_pb_rewriter m_pb2bv; + vector m_weights; + expr_ref_vector m_soft; + model_ref m_model; + params_ref m_params; + symbol m_engine; + public: + sls_solver(ast_manager & m, solver* s, + expr_ref_vector const& soft, + vector const& weights, + params_ref & p): + solver_na2as(m), + m(m), + m_solver(s), + m_bvsls(0), + m_pbsls(0), + m_pb2bv(m), + m_weights(weights), + m_soft(soft) + { + updt_params(p); + } + virtual ~sls_solver() {} + + virtual void updt_params(params_ref & p) { + m_solver->updt_params(p); + m_params.copy(p); + opt_params _p(p); + m_engine = _p.sls_engine(); + } + virtual void collect_param_descrs(param_descrs & r) { + m_solver->collect_param_descrs(r); + } + virtual void collect_statistics(statistics & st) const { + m_solver->collect_statistics(st); + if (m_bvsls) m_bvsls->collect_statistics(st); + if (m_pbsls) m_pbsls->collect_statistics(st); + } + virtual void assert_expr(expr * t) { + m_solver->assert_expr(t); + } + virtual void get_unsat_core(ptr_vector & r) { + m_solver->get_unsat_core(r); + } + virtual void get_model(model_ref & m) { + m = m_model; + } + virtual proof * get_proof() { + return m_solver->get_proof(); + } + virtual std::string reason_unknown() const { + return m_solver->reason_unknown(); + } + virtual void get_labels(svector & r) { + m_solver->get_labels(r); + } + virtual void set_cancel(bool f) { + m_solver->set_cancel(f); + m_pb2bv.set_cancel(f); + #pragma omp critical (sls_solver) + { + if (m_bvsls) { + m_bvsls->set_cancel(f); + } + if (m_pbsls) { + m_pbsls->set_cancel(f); + } + } + } + virtual void set_progress_callback(progress_callback * callback) { + m_solver->set_progress_callback(callback); + } + virtual unsigned get_num_assertions() const { + return m_solver->get_num_assertions(); + } + virtual expr * get_assertion(unsigned idx) const { + return m_solver->get_assertion(idx); + } + virtual void display(std::ostream & out) const { + m_solver->display(out); + // if (m_bvsls) m_bvsls->display(out); + } + + void opt(model_ref& mdl) { + if (m_engine == symbol("pb")) { + pbsls_opt(mdl); + } + else { + bvsls_opt(mdl); + } + } + + static expr_ref soft2bv(expr_ref_vector const& soft, vector const& weights) { + ast_manager& m = soft.get_manager(); + pb::card_pb_rewriter pb2bv(m); + rational upper(1); + expr_ref objective(m); + for (unsigned i = 0; i < weights.size(); ++i) { + upper += weights[i]; + } + expr_ref zero(m), tmp(m); + bv_util bv(m); + expr_ref_vector es(m); + rational num = numerator(upper); + rational den = denominator(upper); + rational maxval = num*den; + unsigned bv_size = maxval.get_num_bits(); + zero = bv.mk_numeral(rational(0), bv_size); + for (unsigned i = 0; i < soft.size(); ++i) { + pb2bv(soft[i], tmp); + es.push_back(m.mk_ite(tmp, bv.mk_numeral(den*weights[i], bv_size), zero)); + } + if (es.empty()) { + objective = bv.mk_numeral(0, bv_size); + } + else { + objective = es[0].get(); + for (unsigned i = 1; i < es.size(); ++i) { + objective = bv.mk_bv_add(objective, es[i].get()); + } + } + return objective; + } + + protected: + typedef bvsls_opt_engine::optimization_result opt_result; + + virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + lbool r = m_solver->check_sat(num_assumptions, assumptions); + if (r == l_true) { + m_solver->get_model(m_model); + opt(m_model); + } + return r; + } + virtual void push_core() { + m_solver->push(); + } + virtual void pop_core(unsigned n) { + m_solver->pop(n); + } + + + private: + // convert soft constraints to bit-vector objective. + + void assertions2sls() { + expr_ref tmp(m); + goal_ref g(alloc(goal, m, true, false)); + for (unsigned i = 0; i < m_solver->get_num_assertions(); ++i) { + m_pb2bv(m_solver->get_assertion(i), tmp); + g->assert_expr(tmp); + } + tactic_ref simplify = mk_nnf_tactic(m); + proof_converter_ref pc; + expr_dependency_ref core(m); + goal_ref_buffer result; + model_converter_ref model_converter; + (*simplify)(g, result, model_converter, pc, core); + SASSERT(result.size() == 1); + goal* r = result[0]; + for (unsigned i = 0; i < r->size(); ++i) { + m_bvsls->assert_expr(r->form(i)); + } + } + + void pbsls_opt(model_ref& mdl) { + #pragma omp critical (sls_solver) + { + if (m_pbsls) { + m_pbsls->reset(); + } + else { + m_pbsls = alloc(smt::pb_sls, m); + } + } + m_pbsls->set_model(mdl); + m_pbsls->updt_params(m_params); + for (unsigned i = 0; i < m_solver->get_num_assertions(); ++i) { + m_pbsls->add(m_solver->get_assertion(i)); + } + for (unsigned i = 0; i < m_soft.size(); ++i) { + m_pbsls->add(m_soft[i].get(), m_weights[i]); + } + (*m_pbsls.get())(); + m_pbsls->get_model(m_model); + mdl = m_model.get(); + } + + void bvsls_opt(model_ref& mdl) { + #pragma omp critical (sls_solver) + { + m_bvsls = alloc(bvsls_opt_engine, m, m_params); + } + assertions2sls(); + expr_ref objective = soft2bv(m_soft, m_weights); + opt_result res(m); + res.is_sat = l_undef; + try { + res = m_bvsls->optimize(objective, mdl, true); + } + catch (...) { + + } + SASSERT(res.is_sat == l_true || res.is_sat == l_undef); + if (res.is_sat == l_true) { + m_bvsls->get_model(m_model); + mdl = m_model.get(); + } + } + + }; +} + +#endif diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp new file mode 100644 index 000000000..bf7a39424 --- /dev/null +++ b/src/opt/opt_solver.cpp @@ -0,0 +1,408 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + opt_solver.cpp + +Abstract: + + Wraps smt::kernel as a solver for optimization + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-16 + +Notes: + + Based directly on smt_solver. + +--*/ +#include +#include "reg_decl_plugins.h" +#include "opt_solver.h" +#include "smt_context.h" +#include "theory_arith.h" +#include "theory_diff_logic.h" +#include "theory_dense_diff_logic.h" +#include "theory_pb.h" +#include "ast_pp.h" +#include "ast_smt_pp.h" +#include "pp_params.hpp" +#include "opt_params.hpp" +#include "model_smt2_pp.h" +#include "stopwatch.h" + +namespace opt { + + opt_solver::opt_solver(ast_manager & mgr, params_ref const & p, + filter_model_converter& fm): + solver_na2as(mgr), + m_params(p), + m_context(mgr, m_params), + m(mgr), + m_fm(fm), + m_objective_terms(m), + m_dump_benchmarks(false), + m_first(true) { + m_params.updt_params(p); + if (m_params.m_case_split_strategy == CS_ACTIVITY_DELAY_NEW) { + m_params.m_relevancy_lvl = 0; + } + } + + unsigned opt_solver::m_dump_count = 0; + + opt_solver::~opt_solver() { + } + + void opt_solver::updt_params(params_ref & _p) { + opt_params p(_p); + m_dump_benchmarks = p.dump_benchmarks(); + m_params.updt_params(_p); + m_context.updt_params(_p); + } + + void opt_solver::collect_param_descrs(param_descrs & r) { + m_context.collect_param_descrs(r); + } + + void opt_solver::collect_statistics(statistics & st) const { + m_context.collect_statistics(st); + } + + void opt_solver::assert_expr(expr * t) { + m_context.assert_expr(t); + } + + void opt_solver::push_core() { + m_context.push(); + } + + void opt_solver::pop_core(unsigned n) { + m_context.pop(n); + } + + void opt_solver::set_logic(symbol const& logic) { + m_logic = logic; + m_context.set_logic(logic); + } + + void opt_solver::ensure_pb() { + smt::theory_id th_id = m.get_family_id("pb"); + smt::theory* th = get_context().get_theory(th_id); + if (!th) { + get_context().register_plugin(alloc(smt::theory_pb, m, m_params)); + } + } + + smt::theory_opt& opt_solver::get_optimizer() { + smt::context& ctx = m_context.get_context(); + smt::theory_id arith_id = m_context.m().get_family_id("arith"); + smt::theory* arith_theory = ctx.get_theory(arith_id); + + if (!arith_theory) { + ctx.register_plugin(alloc(smt::theory_mi_arith, m, m_params)); + arith_theory = ctx.get_theory(arith_id); + SASSERT(arith_theory); + } + if (typeid(smt::theory_mi_arith) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else if (typeid(smt::theory_i_arith) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else if (typeid(smt::theory_inf_arith) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else if (typeid(smt::theory_rdl&) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else if (typeid(smt::theory_idl&) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else if (typeid(smt::theory_dense_mi&) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else if (typeid(smt::theory_dense_i&) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else if (typeid(smt::theory_dense_smi&) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else if (typeid(smt::theory_dense_si&) == typeid(*arith_theory)) { + return dynamic_cast(*arith_theory); + } + else { + UNREACHABLE(); + return dynamic_cast(*arith_theory); + } + } + + bool opt_solver::dump_benchmarks() { + return m_dump_benchmarks; + } + + lbool opt_solver::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + TRACE("opt_verbose", { + tout << "context size: " << m_context.size() << "\n"; + for (unsigned i = 0; i < m_context.size(); ++i) { + tout << mk_pp(m_context.get_formulas()[i], m_context.m()) << "\n"; + } + }); + stopwatch w; + if (dump_benchmarks()) { + w.start(); + std::stringstream file_name; + file_name << "opt_solver" << ++m_dump_count << ".smt2"; + std::ofstream buffer(file_name.str().c_str()); + to_smt2_benchmark(buffer, num_assumptions, assumptions, "opt_solver", ""); + buffer.close(); + IF_VERBOSE(1, verbose_stream() << "(created benchmark: " << file_name.str() << "..."; + verbose_stream().flush();); + } + lbool r; + if (m_first && num_assumptions == 0 && m_context.get_scope_level() == 0) { + r = m_context.setup_and_check(); + } + else { + r = m_context.check(num_assumptions, assumptions); + } + m_first = false; + if (dump_benchmarks()) { + w.stop(); + IF_VERBOSE(1, verbose_stream() << ".. " << r << " " << std::fixed << w.get_seconds() << ")\n";); + } + return r; + } + + void opt_solver::maximize_objectives(expr_ref_vector& blockers) { + expr_ref blocker(m); + for (unsigned i = 0; i < m_objective_vars.size(); ++i) { + maximize_objective(i, blocker); + blockers.push_back(blocker); + } + } + + + /** + \brief maximize the value of objective i in the current state. + Return a predicate that blocks the current maximal value. + + The result of 'maximize' is post-processed. + When maximization involves shared symbols the model produced + by local optimization does not necessarily satisfy combination + constraints (it may not be a real model). + In this case, the model is post-processed (update_model + causes an additional call to final_check to propagate theory equalities + when 'has_shared' is true). + + */ + void opt_solver::maximize_objective(unsigned i, expr_ref& blocker) { + smt::theory_var v = m_objective_vars[i]; + bool has_shared = false; + inf_eps val = get_optimizer().maximize(v, blocker, has_shared); + inf_eps val2; + m_valid_objectives[i] = true; + TRACE("opt", tout << (has_shared?"has shared":"non-shared") << "\n";); + if (m_context.get_context().update_model(has_shared)) { + if (has_shared && val != current_objective_value(i)) { + decrement_value(i, val); + } + else { + set_model(i); + } + } + else { + SASSERT(has_shared); + decrement_value(i, val); + } + m_objective_values[i] = val; + TRACE("opt", { + tout << "objective: " << mk_pp(m_objective_terms[i].get(), m) << "\n"; + tout << "maximal value: " << val << "\n"; + tout << "new condition: " << blocker << "\n"; + model_smt2_pp(tout << "update model:\n", m, *m_models[i], 0); }); + } + + void opt_solver::set_model(unsigned i) { + model_ref mdl; + get_model(mdl); + m_models.set(i, mdl.get()); + } + + lbool opt_solver::decrement_value(unsigned i, inf_eps& val) { + push_core(); + expr_ref ge = mk_ge(i, val); + TRACE("opt", tout << ge << "\n";); + assert_expr(ge); + lbool is_sat = m_context.check(0, 0); + if (is_sat == l_true) { + set_model(i); + } + pop_core(1); + TRACE("opt", tout << is_sat << "\n";); + if (is_sat != l_true) { + // cop-out approximation + if (arith_util(m).is_real(m_objective_terms[i].get())) { + val -= inf_eps(inf_rational(rational(0), true)); + } + else { + val -= inf_eps(inf_rational(rational(1))); + } + m_valid_objectives[i] = false; + } + return is_sat; + + } + + + void opt_solver::get_unsat_core(ptr_vector & r) { + unsigned sz = m_context.get_unsat_core_size(); + for (unsigned i = 0; i < sz; i++) { + r.push_back(m_context.get_unsat_core_expr(i)); + } + } + + void opt_solver::get_model(model_ref & m) { + m_context.get_model(m); + } + + proof * opt_solver::get_proof() { + return m_context.get_proof(); + } + + std::string opt_solver::reason_unknown() const { + return m_context.last_failure_as_string(); + } + + void opt_solver::get_labels(svector & r) { + buffer tmp; + m_context.get_relevant_labels(0, tmp); + r.append(tmp.size(), tmp.c_ptr()); + } + + void opt_solver::set_cancel(bool f) { + m_context.set_cancel(f); + } + + void opt_solver::set_progress_callback(progress_callback * callback) { + m_callback = callback; + m_context.set_progress_callback(callback); + } + + unsigned opt_solver::get_num_assertions() const { + return m_context.size(); + } + + expr * opt_solver::get_assertion(unsigned idx) const { + SASSERT(idx < get_num_assertions()); + return m_context.get_formulas()[idx]; + } + + void opt_solver::display(std::ostream & out) const { + m_context.display(out); + } + + smt::theory_var opt_solver::add_objective(app* term) { + smt::theory_var v = get_optimizer().add_objective(term); + m_objective_vars.push_back(v); + m_objective_values.push_back(inf_eps(rational(-1), inf_rational())); + m_objective_terms.push_back(term); + m_valid_objectives.push_back(true); + m_models.push_back(0); + return v; + } + + vector const& opt_solver::get_objective_values() { + return m_objective_values; + } + + inf_eps const& opt_solver::saved_objective_value(unsigned i) { + return m_objective_values[i]; + } + + inf_eps opt_solver::current_objective_value(unsigned i) { + smt::theory_var v = m_objective_vars[i]; + return get_optimizer().value(v); + } + + expr_ref opt_solver::mk_ge(unsigned var, inf_eps const& val) { + smt::theory_opt& opt = get_optimizer(); + smt::theory_var v = m_objective_vars[var]; + + if (typeid(smt::theory_inf_arith) == typeid(opt)) { + smt::theory_inf_arith& th = dynamic_cast(opt); + return th.mk_ge(m_fm, v, val); + } + + if (typeid(smt::theory_mi_arith) == typeid(opt)) { + smt::theory_mi_arith& th = dynamic_cast(opt); + SASSERT(val.is_finite()); + return th.mk_ge(m_fm, v, val.get_numeral()); + } + + if (typeid(smt::theory_i_arith) == typeid(opt)) { + SASSERT(val.is_finite()); + SASSERT(val.get_infinitesimal().is_zero()); + smt::theory_i_arith& th = dynamic_cast(opt); + return th.mk_ge(m_fm, v, val.get_rational()); + } + + if (typeid(smt::theory_idl) == typeid(opt)) { + smt::theory_idl& th = dynamic_cast(opt); + return th.mk_ge(m_fm, v, val.get_rational()); + } + + if (typeid(smt::theory_rdl) == typeid(opt) && + val.get_infinitesimal().is_zero()) { + smt::theory_rdl& th = dynamic_cast(opt); + return th.mk_ge(m_fm, v, val.get_rational()); + } + + // difference logic? + return expr_ref(m.mk_true(), m); + } + + void opt_solver::reset_objectives() { + m_objective_vars.reset(); + m_objective_values.reset(); + m_objective_terms.reset(); + m_valid_objectives.reset(); + } + + opt_solver& opt_solver::to_opt(solver& s) { + if (typeid(opt_solver) != typeid(s)) { + throw default_exception("BUG: optimization context has not been initialized correctly"); + } + return dynamic_cast(s); + } + + + void opt_solver::to_smt2_benchmark( + std::ofstream & buffer, + unsigned num_assumptions, + expr * const * assumptions, + char const * name, + char const * logic, + char const * status, + char const * attributes) { + ast_smt_pp pp(m); + pp.set_benchmark_name(name); + pp.set_logic(logic); + pp.set_status(status); + pp.add_attributes(attributes); + pp_params params; + pp.set_simplify_implies(params.simplify_implies()); + + for (unsigned i = 0; i < num_assumptions; ++i) { + pp.add_assumption(assumptions[i]); + } + for (unsigned i = 0; i < get_num_assertions(); ++i) { + pp.add_assumption(get_assertion(i)); + } + pp.display_smt2(buffer, m.mk_true()); + } + + +} diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h new file mode 100644 index 000000000..9e74e9127 --- /dev/null +++ b/src/opt/opt_solver.h @@ -0,0 +1,141 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + opt_solver.h + +Abstract: + + Wraps smt::kernel as a solver for optimization + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-16 + +Notes: + + Based directly on smt_solver. + +--*/ +#ifndef OPT_SOLVER_H_ +#define OPT_SOLVER_H_ + +#include"inf_rational.h" +#include"inf_eps_rational.h" +#include"ast.h" +#include"params.h" +#include"solver_na2as.h" +#include"smt_kernel.h" +#include"smt_params.h" +#include"smt_types.h" +#include"theory_opt.h" +#include"filter_model_converter.h" + +namespace opt { + + typedef inf_eps_rational inf_eps; + + // Adjust bound bound |-> m_offset + (m_negate?-1:1)*bound + class adjust_value { + rational m_offset; + bool m_negate; + public: + adjust_value(rational const& offset, bool neg): + m_offset(offset), + m_negate(neg) + {} + adjust_value(): m_offset(0), m_negate(false) {} + void set_offset(rational const& o) { m_offset = o; } + void set_negate(bool neg) { m_negate = neg; } + rational const& get_offset() const { return m_offset; } + bool get_negate() { return m_negate; } + inf_eps operator()(inf_eps const& r) const { + inf_eps result = r; + if (m_negate) result.neg(); + result += m_offset; + return result; + } + rational operator()(rational const& r) const { + rational result = r; + if (m_negate) result.neg(); + result += m_offset; + return result; + } + }; + + + class opt_solver : public solver_na2as { + private: + smt_params m_params; + smt::kernel m_context; + ast_manager& m; + filter_model_converter& m_fm; + progress_callback * m_callback; + symbol m_logic; + svector m_objective_vars; + vector m_objective_values; + sref_vector m_models; + expr_ref_vector m_objective_terms; + svector m_valid_objectives; + bool m_dump_benchmarks; + static unsigned m_dump_count; + statistics m_stats; + bool m_first; + public: + opt_solver(ast_manager & m, params_ref const & p, filter_model_converter& fm); + virtual ~opt_solver(); + + virtual void updt_params(params_ref & p); + virtual void collect_param_descrs(param_descrs & r); + virtual void collect_statistics(statistics & st) const; + virtual void assert_expr(expr * t); + virtual void push_core(); + virtual void pop_core(unsigned n); + virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions); + virtual void get_unsat_core(ptr_vector & r); + virtual void get_model(model_ref & _m); + virtual proof * get_proof(); + virtual std::string reason_unknown() const; + virtual void get_labels(svector & r); + virtual void set_cancel(bool f); + virtual void set_progress_callback(progress_callback * callback); + virtual unsigned get_num_assertions() const; + virtual expr * get_assertion(unsigned idx) const; + virtual void display(std::ostream & out) const; + void set_logic(symbol const& logic); + + smt::theory_var add_objective(app* term); + void reset_objectives(); + void maximize_objective(unsigned i, expr_ref& blocker); + void maximize_objectives(expr_ref_vector& blockers); + inf_eps const & saved_objective_value(unsigned obj_index); + inf_eps current_objective_value(unsigned obj_index); + model* get_model(unsigned obj_index) { return m_models[obj_index]; } + bool objective_is_model_valid(unsigned obj_index) const { + return m_valid_objectives[obj_index]; + } + + vector const& get_objective_values(); + expr_ref mk_ge(unsigned obj_index, inf_eps const& val); + + static opt_solver& to_opt(solver& s); + bool dump_benchmarks(); + + smt::context& get_context() { return m_context.get_context(); } // used by weighted maxsat. + void ensure_pb(); + + smt::theory_opt& get_optimizer(); + + void to_smt2_benchmark(std::ofstream & buffer, + unsigned num_assumptions, expr * const * assumptions, + char const * name = "benchmarks", + char const * logic = "", char const * status = "unknown", char const * attributes = ""); + + private: + lbool decrement_value(unsigned i, inf_eps& val); + void set_model(unsigned i); + }; +} + +#endif diff --git a/src/opt/optsmt.cpp b/src/opt/optsmt.cpp new file mode 100644 index 000000000..02effa337 --- /dev/null +++ b/src/opt/optsmt.cpp @@ -0,0 +1,451 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + optsmt.cpp + +Abstract: + + Objective optimization method. + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-16 + +Notes: + + Suppose we obtain solution t1 = k1, ..., tn = kn-epsilon + Assert: + t1 > k1 \/ t2 > k2 \/ ... \/ tn >= kn + If this solution is satisfiable, then for each t_i, maximize the + assignment and assert the new frontier. + Claim: we don't necessarily have to freeze assignments of + t_i when optimizing assignment for t_j + because the state will always satisfy the disjunction. + If one of the k_i is unbounded, then omit a disjunction for it. + + +--*/ + +#include +#include "optsmt.h" +#include "opt_solver.h" +#include "arith_decl_plugin.h" +#include "theory_arith.h" +#include "ast_pp.h" +#include "model_pp.h" +#include "th_rewriter.h" +#include "opt_params.hpp" + +namespace opt { + + + void optsmt::set_cancel(bool f) { + TRACE("opt", tout << "set cancel: " << f << "\n";); + m_cancel = f; + } + + void optsmt::set_max(vector& dst, vector const& src, expr_ref_vector& fmls) { + for (unsigned i = 0; i < src.size(); ++i) { + if (src[i] >= dst[i]) { + dst[i] = src[i]; + m_models.set(i, m_s->get_model(i)); + m_lower_fmls[i] = fmls[i].get(); + if (dst[i].is_pos() && !dst[i].is_finite()) { // review: likely done already. + m_lower_fmls[i] = m.mk_false(); + fmls[i] = m.mk_false(); + } + } + else if (src[i] < dst[i] && !m.is_true(m_lower_fmls[i].get())) { + fmls[i] = m_lower_fmls[i].get(); + } + } + } + + /* + Enumerate locally optimal assignments until fixedpoint. + */ + lbool optsmt::basic_opt() { + lbool is_sat = l_true; + + expr_ref bound(m.mk_true(), m), tmp(m); + expr* vars[1]; + + solver::scoped_push _push(*m_s); + while (is_sat == l_true && !m_cancel) { + + tmp = m.mk_fresh_const("b", m.mk_bool_sort()); + vars[0] = tmp; + bound = m.mk_implies(tmp, bound); + m_s->assert_expr(bound); + is_sat = m_s->check_sat(1, vars); + if (is_sat == l_true) { + bound = update_lower(); + } + } + + if (m_cancel || is_sat == l_undef) { + return l_undef; + } + + // set the solution tight. + for (unsigned i = 0; i < m_lower.size(); ++i) { + m_upper[i] = m_lower[i]; + } + + return l_true; + } + + /* + Enumerate locally optimal assignments until fixedpoint. + */ + lbool optsmt::farkas_opt() { + smt::theory_opt& opt = m_s->get_optimizer(); + + if (typeid(smt::theory_inf_arith) != typeid(opt)) { + return l_undef; + } + + lbool is_sat = l_true; + + while (is_sat == l_true && !m_cancel) { + is_sat = update_upper(); + } + + if (m_cancel || is_sat == l_undef) { + return l_undef; + } + + // set the solution tight. + for (unsigned i = 0; i < m_lower.size(); ++i) { + m_upper[i] = m_lower[i]; + } + + return l_true; + } + + lbool optsmt::symba_opt() { + smt::theory_opt& opt = m_s->get_optimizer(); + + if (typeid(smt::theory_inf_arith) != typeid(opt)) { + return l_undef; + } + + + expr_ref_vector ors(m), disj(m); + expr_ref fml(m), bound(m.mk_true(), m), tmp(m); + expr* vars[1]; + { + for (unsigned i = 0; i < m_upper.size(); ++i) { + ors.push_back(m_s->mk_ge(i, m_upper[i])); + } + + + fml = m.mk_or(ors.size(), ors.c_ptr()); + tmp = m.mk_fresh_const("b", m.mk_bool_sort()); + fml = m.mk_implies(tmp, fml); + vars[0] = tmp; + lbool is_sat = l_true; + + solver::scoped_push _push(*m_s); + while (!m_cancel) { + m_s->assert_expr(fml); + TRACE("opt", tout << fml << "\n";); + is_sat = m_s->check_sat(1,vars); + if (is_sat == l_true) { + disj.reset(); + m_s->maximize_objectives(disj); + m_s->get_model(m_model); + for (unsigned i = 0; i < ors.size(); ++i) { + expr_ref tmp(m); + m_model->eval(ors[i].get(), tmp); + if (m.is_true(tmp)) { + m_lower[i] = m_upper[i]; + ors[i] = m.mk_false(); + disj[i] = m.mk_false(); + } + } + set_max(m_lower, m_s->get_objective_values(), disj); + fml = m.mk_or(ors.size(), ors.c_ptr()); + tmp = m.mk_fresh_const("b", m.mk_bool_sort()); + fml = m.mk_implies(tmp, fml); + vars[0] = tmp; + } + else if (is_sat == l_undef) { + return l_undef; + } + else { + break; + } + } + } + bound = m.mk_or(m_lower_fmls.size(), m_lower_fmls.c_ptr()); + m_s->assert_expr(bound); + + if (m_cancel) { + return l_undef; + } + return basic_opt(); + } + + void optsmt::update_lower(unsigned idx, inf_eps const& v) { + TRACE("opt", tout << "v" << idx << " >= " << v << "\n";); + m_lower_fmls[idx] = m_s->mk_ge(idx, v); + m_lower[idx] = v; + } + + void optsmt::update_upper(unsigned idx, inf_eps const& v) { + TRACE("opt", tout << "v" << idx << " <= " << v << "\n";); + m_upper[idx] = v; + } + + expr_ref optsmt::update_lower() { + expr_ref_vector disj(m); + m_s->get_model(m_model); + m_s->maximize_objectives(disj); + set_max(m_lower, m_s->get_objective_values(), disj); + TRACE("opt", + for (unsigned i = 0; i < m_lower.size(); ++i) { + tout << m_lower[i] << " "; + } + tout << "\n"; + model_pp(tout, *m_model); + ); + IF_VERBOSE(2, verbose_stream() << "(optsmt.lower "; + for (unsigned i = 0; i < m_lower.size(); ++i) { + verbose_stream() << m_lower[i] << " "; + } + verbose_stream() << ")\n";); + IF_VERBOSE(3, verbose_stream() << disj << "\n";); + IF_VERBOSE(3, model_pp(verbose_stream(), *m_model);); + + return expr_ref(m.mk_or(disj.size(), disj.c_ptr()), m); + } + + lbool optsmt::update_upper() { + smt::theory_opt& opt = m_s->get_optimizer(); + SASSERT(typeid(smt::theory_inf_arith) == typeid(opt)); + smt::theory_inf_arith& th = dynamic_cast(opt); + expr_ref bound(m); + expr_ref_vector bounds(m); + + solver::scoped_push _push(*m_s); + + // + // NB: we have to create all bound expressions before calling check_sat + // because the state after check_sat is not at base level. + // + + vector mid; + + for (unsigned i = 0; i < m_lower.size() && !m_cancel; ++i) { + if (m_lower[i] < m_upper[i]) { + mid.push_back((m_upper[i]+m_lower[i])/rational(2)); + bound = m_s->mk_ge(i, mid[i]); + bounds.push_back(bound); + } + else { + bounds.push_back(0); + mid.push_back(inf_eps()); + } + } + bool progress = false; + for (unsigned i = 0; i < m_lower.size() && !m_cancel; ++i) { + if (m_lower[i] <= mid[i] && mid[i] <= m_upper[i] && m_lower[i] < m_upper[i]) { + th.enable_record_conflict(bounds[i].get()); + lbool is_sat = m_s->check_sat(1, bounds.c_ptr() + i); + switch(is_sat) { + case l_true: + IF_VERBOSE(2, verbose_stream() << "(optsmt lower bound for v" << m_vars[i] << " := " << m_upper[i] << ")\n";); + m_lower[i] = mid[i]; + th.enable_record_conflict(0); + m_s->assert_expr(update_lower()); + break; + case l_false: + IF_VERBOSE(2, verbose_stream() << "(optsmt conflict: " << th.conflict_minimize() << ") \n";); + if (!th.conflict_minimize().is_finite()) { + // bounds is not in the core. The context is unsat. + m_upper[i] = m_lower[i]; + return l_false; + } + else { + m_upper[i] = std::min(m_upper[i], th.conflict_minimize()); + } + break; + default: + th.enable_record_conflict(0); + return l_undef; + } + th.enable_record_conflict(0); + progress = true; + } + } + if (m_cancel) { + return l_undef; + } + if (!progress) { + return l_false; + } + return l_true; + } + + void optsmt::setup(opt_solver& solver) { + m_s = &solver; + solver.reset_objectives(); + m_vars.reset(); + + // force base level + { + solver::scoped_push _push(solver); + } + + for (unsigned i = 0; i < m_objs.size(); ++i) { + smt::theory_var v = solver.add_objective(m_objs[i].get()); + if (v == smt::null_theory_var) { + std::ostringstream out; + out << "Objective function '" << mk_pp(m_objs[i].get(), m) << "' is not supported"; + throw default_exception(out.str()); + } + m_vars.push_back(v); + } + } + + lbool optsmt::lex(unsigned obj_index, bool is_maximize) { + TRACE("opt", tout << "optsmt:lex\n";); + solver::scoped_push _push(*m_s); + SASSERT(obj_index < m_vars.size()); + return basic_lex(obj_index, is_maximize); + } + + lbool optsmt::basic_lex(unsigned obj_index, bool is_maximize) { + lbool is_sat = l_true; + expr_ref block(m), tmp(m); + + for (unsigned i = 0; i < obj_index; ++i) { + commit_assignment(i); + } + while (is_sat == l_true && !m_cancel) { + is_sat = m_s->check_sat(0, 0); + if (is_sat != l_true) break; + + m_s->maximize_objective(obj_index, block); + m_s->get_model(m_model); + inf_eps obj = m_s->saved_objective_value(obj_index); + if (obj > m_lower[obj_index]) { + m_lower[obj_index] = obj; + IF_VERBOSE(1, + if (is_maximize) + verbose_stream() << "(optsmt lower bound: " << obj << ")\n"; + else + verbose_stream() << "(optsmt upper bound: " << (-obj) << ")\n"; + ); + for (unsigned i = obj_index+1; i < m_vars.size(); ++i) { + m_s->maximize_objective(i, tmp); + m_lower[i] = m_s->saved_objective_value(i); + } + } + TRACE("opt", tout << "strengthen bound: " << block << "\n";); + m_s->assert_expr(block); + + // TBD: only works for simplex + // blocking formula should be extracted based + // on current state. + } + + if (m_cancel || is_sat == l_undef) { + TRACE("opt", tout << "undef: " << m_cancel << " " << is_sat << "\n";); + return l_undef; + } + + // set the solution tight. + m_upper[obj_index] = m_lower[obj_index]; + for (unsigned i = obj_index+1; i < m_lower.size(); ++i) { + m_lower[i] = inf_eps(rational(-1), inf_rational(0)); + } + return l_true; + } + + + /** + Takes solver with hard constraints added. + Returns an optimal assignment to objective functions. + */ + lbool optsmt::box() { + lbool is_sat = l_true; + if (m_vars.empty()) { + return is_sat; + } + // assertions added during search are temporary. + solver::scoped_push _push(*m_s); + if (m_optsmt_engine == symbol("farkas")) { + is_sat = farkas_opt(); + } + else if (m_optsmt_engine == symbol("symba")) { + is_sat = symba_opt(); + } + else { + is_sat = basic_opt(); + } + return is_sat; + } + + + inf_eps optsmt::get_lower(unsigned i) const { + if (i >= m_lower.size()) return inf_eps(); + return m_lower[i]; + } + + bool optsmt::objective_is_model_valid(unsigned index) const { + return m_s->objective_is_model_valid(index); + } + + inf_eps optsmt::get_upper(unsigned i) const { + if (i >= m_upper.size()) return inf_eps(); + return m_upper[i]; + } + + void optsmt::get_model(model_ref& mdl) { + mdl = m_model.get(); + } + + // force lower_bound(i) <= objective_value(i) + void optsmt::commit_assignment(unsigned i) { + inf_eps lo = m_lower[i]; + TRACE("opt", tout << "set lower bound of " << mk_pp(m_objs[i].get(), m) << " to: " << lo << "\n"; + tout << get_lower(i) << ":" << get_upper(i) << "\n";); + // Only assert bounds for bounded objectives + if (lo.is_finite()) { + m_s->assert_expr(m_s->mk_ge(i, lo)); + } + } + + unsigned optsmt::add(app* t) { + expr_ref t1(t, m), t2(m); + th_rewriter rw(m); + rw(t1, t2); + SASSERT(is_app(t2)); + m_objs.push_back(to_app(t2)); + m_lower.push_back(inf_eps(rational(-1),inf_rational(0))); + m_upper.push_back(inf_eps(rational(1), inf_rational(0))); + m_lower_fmls.push_back(m.mk_true()); + m_models.push_back(0); + return m_objs.size()-1; + } + + void optsmt::updt_params(params_ref& p) { + opt_params _p(p); + m_optsmt_engine = _p.optsmt_engine(); + } + + void optsmt::reset() { + m_lower.reset(); + m_upper.reset(); + m_objs.reset(); + m_vars.reset(); + m_model.reset(); + m_lower_fmls.reset(); + m_s = 0; + } +} + diff --git a/src/opt/optsmt.h b/src/opt/optsmt.h new file mode 100644 index 000000000..10ea9f11c --- /dev/null +++ b/src/opt/optsmt.h @@ -0,0 +1,91 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + optsmt.h + +Abstract: + + Objective optimization method. + +Author: + + Anh-Dung Phan (t-anphan) 2013-10-16 + +Notes: + +--*/ +#ifndef OPTSMT_H_ +#define OPTSMT_H_ + +#include "opt_solver.h" + +namespace opt { + /** + Takes solver with hard constraints added. + Returns an optimal assignment to objective functions. + */ + + class optsmt { + ast_manager& m; + opt_solver* m_s; + volatile bool m_cancel; + vector m_lower; + vector m_upper; + app_ref_vector m_objs; + expr_ref_vector m_lower_fmls; + svector m_vars; + symbol m_optsmt_engine; + model_ref m_model; + sref_vector m_models; + public: + optsmt(ast_manager& m): + m(m), m_s(0), m_cancel(false), m_objs(m), m_lower_fmls(m) {} + + void setup(opt_solver& solver); + + lbool box(); + + lbool lex(unsigned obj_index, bool is_maximize); + + unsigned add(app* t); + + void set_cancel(bool f); + + void updt_params(params_ref& p); + + unsigned get_num_objectives() const { return m_objs.size(); } + void commit_assignment(unsigned index); + inf_eps get_lower(unsigned index) const; + inf_eps get_upper(unsigned index) const; + bool objective_is_model_valid(unsigned index) const; + void get_model(model_ref& mdl); + model* get_model(unsigned index) const { return m_models[index]; } + + void update_lower(unsigned idx, inf_eps const& r); + void update_upper(unsigned idx, inf_eps const& r); + + void reset(); + + private: + + lbool basic_opt(); + + lbool symba_opt(); + + lbool basic_lex(unsigned idx, bool is_maximize); + + lbool farkas_opt(); + + void set_max(vector& dst, vector const& src, expr_ref_vector& fmls); + + expr_ref update_lower(); + + lbool update_upper(); + + }; + +}; + +#endif diff --git a/src/opt/pb_sls.cpp b/src/opt/pb_sls.cpp new file mode 100644 index 000000000..05bdd5cf8 --- /dev/null +++ b/src/opt/pb_sls.cpp @@ -0,0 +1,741 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + pb_sls.cpp + +Abstract: + + SLS for PB optimization. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-03-18 + +Notes: + +--*/ +#include "pb_sls.h" +#include "smt_literal.h" +#include "ast_pp.h" +#include "th_rewriter.h" +#include "sat_sls.h" + +namespace smt { + struct pb_sls::imp { + + struct clause { + literal_vector m_lits; + scoped_mpz_vector m_weights; + scoped_mpz m_k; + scoped_mpz m_value; + bool m_eq; + clause(unsynch_mpz_manager& m): + m_weights(m), + m_k(m), + m_value(m), + m_eq(true) + {} + clause(clause const& cls): + m_lits(cls.m_lits), + m_weights(cls.m_weights.m()), + m_k(cls.m_k), + m_value(cls.m_value), + m_eq(cls.m_eq) { + for (unsigned i = 0; i < cls.m_weights.size(); ++i) { + m_weights.push_back(cls.m_weights[i]); + } + } + }; + + struct stats { + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + unsigned m_num_flips; + unsigned m_num_improvements; + }; + + ast_manager& m; + pb_util pb; + unsynch_mpz_manager mgr; + th_rewriter m_rewrite; + volatile bool m_cancel; + vector m_clauses; // clauses to be satisfied + expr_ref_vector m_orig_clauses; // for debugging + model_ref m_orig_model; // for debugging + vector m_soft; // soft constraints + vector m_weights; // weights of soft constraints + rational m_penalty; // current penalty of soft constraints + rational m_best_penalty; + vector m_hard_occ, m_soft_occ; // variable occurrence + svector m_assignment; // current assignment. + svector m_best_assignment; + expr_ref_vector m_trail; + obj_map m_decl2var; // map declarations to Boolean variables. + ptr_vector m_var2decl; // reverse map + sat::index_set m_hard_false; // list of hard clauses that are false. + sat::index_set m_soft_false; // list of soft clauses that are false. + unsigned m_max_flips; // maximal number of flips + unsigned m_non_greedy_percent; // percent of moves to do non-greedy style + random_gen m_rng; + scoped_mpz one; + stats m_stats; + + imp(ast_manager& m): + m(m), + pb(m), + m_rewrite(m), + m_cancel(false), + m_orig_clauses(m), + m_trail(m), + one(mgr) + { + reset(); + one = mpz(1); + } + + ~imp() { + } + + void reset() { + init_max_flips(); + m_non_greedy_percent = 30; + m_decl2var.reset(); + m_var2decl.reset(); + m_assignment.reset(); + m_hard_occ.reset(); + m_soft_occ.reset(); + m_clauses.reset(); + m_orig_clauses.reset(); + m_soft.reset(); + m_weights.reset(); + m_trail.reset(); + m_decl2var.insert(m.mk_true(), 0); + m_var2decl.push_back(m.mk_true()); + m_assignment.push_back(true); + m_hard_occ.push_back(unsigned_vector()); + m_soft_occ.push_back(unsigned_vector()); + } + + void init_max_flips() { + m_max_flips = 200; + } + + void add(expr* f) { + clause cls(mgr); + if (compile_clause(f, cls)) { + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); + } + } + void add(expr* f, rational const& w) { + clause cls(mgr); + if (compile_clause(f, cls)) { + m_soft.push_back(cls); + m_weights.push_back(w); + } + } + + void set_model(model_ref & mdl) { + m_orig_model = mdl; + for (unsigned i = 0; i < m_var2decl.size(); ++i) { + expr_ref tmp(m); + VERIFY(mdl->eval(m_var2decl[i], tmp)); + m_assignment[i] = m.is_true(tmp); + } + } + + lbool operator()() { + init(); + IF_VERBOSE(1, verbose_stream() << "(pb.sls initial penalty: " << m_best_penalty << ")\n"; + verbose_stream() << "(pb.sls violated: " << m_hard_false.num_elems() + << " penalty: " << m_penalty << ")\n";); + svector assignment(m_assignment); + for (unsigned round = 0; round < 40; ++round) { + init_max_flips(); + while (m_max_flips > 0) { + --m_max_flips; + literal lit = flip(); + if (m_cancel) { + return l_undef; + } + IF_VERBOSE(3, verbose_stream() + << "(pb.sls violated: " << m_hard_false.num_elems() + << " penalty: " << m_penalty << " " << lit << ")\n";); + if (m_hard_false.empty() && m_best_penalty.is_zero()) { + break; + } + } + if (m_hard_false.empty() && m_best_penalty.is_zero()) { + break; + } + IF_VERBOSE(1, verbose_stream() << "(pb.sls best penalty " << m_best_penalty << ")\n";); + if (!m_best_assignment.empty()) { + assignment.reset(); + assignment.append(m_best_assignment); + round = 0; + } + m_assignment.reset(); + m_assignment.append(assignment); + m_best_assignment.reset(); + m_soft_false.reset(); + m_hard_false.reset(); + m_penalty.reset(); + for (unsigned i = 0; i < m_soft.size(); ++i) { + if (!eval(m_soft[i])) { + m_soft_false.insert(i); + m_penalty += m_weights[i]; + } + } + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (!eval(m_clauses[i])) { + m_hard_false.insert(i); + } + } + } + m_assignment.reset(); + m_assignment.append(assignment); + m_penalty = m_best_penalty; + return l_true; + } + + bool get_value(literal l) { + return l.sign() ^ m_assignment[l.var()]; + } + void set_cancel(bool f) { + m_cancel = f; + } + void get_model(model_ref& mdl) { + mdl = alloc(model, m); + for (unsigned i = 1; i < m_var2decl.size(); ++i) { + expr* d = m_var2decl[i]; + if (is_uninterp_const(d)) { + mdl->register_decl(to_app(d)->get_decl(), m_assignment[i] ? m.mk_true() : m.mk_false()); + } + } + } + + void collect_statistics(::statistics& st) const { + st.update("sls.num_flips", m_stats.m_num_flips); + st.update("sls.num_improvements", m_stats.m_num_improvements); + } + + void updt_params(params_ref& p) { + } + + bool soft_holds(unsigned index) { + return eval(m_soft[index]); + } + + void display(std::ostream& out, clause const& cls) { + scoped_mpz w(mgr); + for (unsigned i = 0; i < cls.m_lits.size(); ++i) { + w = cls.m_weights[i]; + out << w << "*" << cls.m_lits[i] << " "; + out << "(" << mk_pp(m_var2decl[cls.m_lits[i].var()], m) << ") "; + if (i + 1 < cls.m_lits.size()) { + out << "+ "; + } + } + out << "(" << cls.m_value << ") "; + if (cls.m_eq) { + out << "= "; + } + else { + out << ">= "; + } + out << cls.m_k << "\n"; + } + + void display(std::ostream& out) { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + display(out, m_clauses[i]); + } + out << "soft:\n"; + for (unsigned i = 0; i < m_soft.size(); ++i) { + display(out << m_weights[i] << ": ", m_soft[i]); + } + for (unsigned i = 0; i < m_assignment.size(); ++i) { + out << literal(i) << ": " << mk_pp(m_var2decl[i], m) << " |-> " << (m_assignment[i]?"true":"false") << "\n"; + } + } + + bool eval(clause& cls) { + unsigned sz = cls.m_lits.size(); + cls.m_value.reset(); + for (unsigned i = 0; i < sz; ++i) { + if (get_value(cls.m_lits[i])) { + cls.m_value += cls.m_weights[i]; + } + } + if (cls.m_eq) { + return cls.m_value == cls.m_k; + } + else { + return cls.m_value >= cls.m_k; + } + } + + void init_occ(vector const& clauses, vector & occ) { + for (unsigned i = 0; i < clauses.size(); ++i) { + clause const& cls = clauses[i]; + for (unsigned j = 0; j < cls.m_lits.size(); ++j) { + literal lit = cls.m_lits[j]; + if (occ.size() <= static_cast(lit.var())) occ.resize(lit.var() + 1); + occ[lit.var()].push_back(i); + } + } + } + + void init() { + m_best_assignment.reset(); + m_best_penalty.reset(); + m_hard_false.reset(); + m_hard_occ.reset(); + m_soft_false.reset(); + m_soft_occ.reset(); + m_penalty.reset(); + for (unsigned i = 0; i <= m_var2decl.size(); ++i) { + m_soft_occ.push_back(unsigned_vector()); + m_hard_occ.push_back(unsigned_vector()); + } + + // initialize the occurs vectors. + init_occ(m_clauses, m_hard_occ); + init_occ(m_soft, m_soft_occ); + + // add clauses that are false. + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (!eval(m_clauses[i])) { + m_hard_false.insert(i); + expr_ref tmp(m); + VERIFY(m_orig_model->eval(m_orig_clauses[i].get(), tmp)); + IF_VERBOSE(0, + verbose_stream() << "original evaluation: " << tmp << "\n"; + verbose_stream() << mk_pp(m_orig_clauses[i].get(), m) << "\n"; + display(verbose_stream(), m_clauses[i]);); + } + } + for (unsigned i = 0; i < m_soft.size(); ++i) { + if (!eval(m_soft[i])) { + m_soft_false.insert(i); + m_penalty += m_weights[i]; + } + } + m_best_penalty = m_penalty; + TRACE("opt", display(tout);); + } + + literal flip() { + m_stats.m_num_flips++; + literal result; + if (m_hard_false.empty()) { + result = flip_soft(); + } + else { + result = flip_hard(); + } + if (m_hard_false.empty() && m_best_penalty > m_penalty) { + IF_VERBOSE(1, verbose_stream() << "(pb.sls improved bound " << m_penalty << ")\n";); + m_best_assignment.reset(); + m_best_assignment.append(m_assignment); + m_best_penalty = m_penalty; + m_stats.m_num_improvements++; + init_max_flips(); + } + if (!m_assignment[result.var()]) { + result.neg(); + } + return result; + } + + literal flip_hard() { + SASSERT(!m_hard_false.empty()); + literal lit; + clause const& cls = pick_hard_clause(); + // IF_VERBOSE(1, display(verbose_stream(), cls);); + int break_count; + int min_bc = INT_MAX; + unsigned min_bc_index = 0; + for (unsigned i = 0; i < cls.m_lits.size(); ++i) { + lit = cls.m_lits[i]; + break_count = flip(lit); + if (break_count < min_bc) { + min_bc = break_count; + min_bc_index = i; + } + else if (break_count == min_bc && m_rng(5) == 1) { + min_bc_index = i; + } + int new_break_count = flip(~lit); + if (-break_count != new_break_count) { + verbose_stream() << lit << "\n"; + IF_VERBOSE(0, display(verbose_stream(), cls);); + display(verbose_stream()); + exit(0); + } + // VERIFY(-break_count == flip(~lit)); + } + if (m_rng(100) <= m_non_greedy_percent) { + lit = cls.m_lits[m_rng(cls.m_lits.size())]; + } + else { + lit = cls.m_lits[min_bc_index]; + } + flip(lit); + return lit; + } + + literal flip_soft() { + literal lit; + clause const& cls = pick_soft_clause(); + int break_count; + int min_bc = INT_MAX; + unsigned min_bc_index = 0; + rational penalty = m_penalty; + rational min_penalty = penalty; + for (unsigned i = 0; i < cls.m_lits.size(); ++i) { + lit = cls.m_lits[i]; + break_count = flip(lit); + SASSERT(break_count >= 0); + if (break_count == 0 && penalty > m_penalty) { + return lit; + } + if ((break_count < min_bc) || + (break_count == min_bc && m_penalty < min_penalty)) { + min_bc = break_count; + min_bc_index = i; + min_penalty = m_penalty; + } + VERIFY(-break_count == flip(~lit)); + } + if (m_rng(100) <= m_non_greedy_percent) { + lit = cls.m_lits[m_rng(cls.m_lits.size())]; + } + else { + // just do a greedy move: + lit = cls.m_lits[min_bc_index]; + } + flip(lit); + return lit; + } + + // + // TODO: alternate version: loop over soft clauses and see if there is a flip that + // reduces the penalty while preserving the hard constraints. + // + + // crude selection strategy. + clause const& pick_hard_clause() { + SASSERT(!m_hard_false.empty()); + return m_clauses[m_hard_false.choose(m_rng)]; + } + + clause const& pick_soft_clause() { + SASSERT(!m_soft_false.empty()); + return m_soft[m_soft_false.choose(m_rng)]; + } + + int flip(literal l) { + m_assignment[l.var()] = !m_assignment[l.var()]; + int break_count = 0; + unsigned_vector const& occh = m_hard_occ[l.var()]; + scoped_mpz value(mgr); + for (unsigned i = 0; i < occh.size(); ++i) { + unsigned j = occh[i]; + clause& cls = m_clauses[j]; + value = cls.m_value; + if (eval(cls)) { + if (m_hard_false.contains(j)) { + break_count--; + m_hard_false.remove(j); + } + } + else { + if (!m_hard_false.contains(j)) { + break_count++; + m_hard_false.insert(j); + } + else if (value < m_clauses[j].m_value) { + } + } + } + unsigned_vector const& occs = m_soft_occ[l.var()]; + for (unsigned i = 0; i < occs.size(); ++i) { + unsigned j = occs[i]; + if (eval(m_soft[j])) { + if (m_soft_false.contains(j)) { + m_penalty -= m_weights[j]; + m_soft_false.remove(j); + } + } + else { + if (!m_soft_false.contains(j)) { + m_penalty += m_weights[j]; + m_soft_false.insert(j); + } + } + } + + TRACE("opt", tout << "flip: " << l << " num false: " << m_hard_false.num_elems() + << " penalty: " << m_penalty << " break count: " << break_count << "\n";); + return break_count; + } + + literal mk_aux_literal(expr* f) { + unsigned var; + expr_ref tmp(m); + if (!m_decl2var.find(f, var)) { + var = m_hard_occ.size(); + SASSERT(m_var2decl.size() == var); + SASSERT(m_soft_occ.size() == var); + m_hard_occ.push_back(unsigned_vector()); + m_soft_occ.push_back(unsigned_vector()); + VERIFY(m_orig_model->eval(f, tmp)); + m_assignment.push_back(m.is_true(tmp)); + m_decl2var.insert(f, var); + m_var2decl.push_back(f); + } + return literal(var); + } + void pad(scoped_mpz_vector& vec, unsigned sz, mpz& val) { + for (unsigned i = 0; i < sz; ++i) { + vec.push_back(val); + } + } + literal mk_literal(expr* f) { + if (m.is_not(f, f)) { + literal result = mk_literal(f); + if (result != null_literal) { + result.neg(); + } + return result; + } + if (is_uninterp_const(f)) + return mk_aux_literal(to_app(f)); + if (m.is_true(f)) + return true_literal; + if (m.is_false(f)) + return false_literal; + if (m.is_and(f)) { + literal_vector lits; + app* g = to_app(f); + for (unsigned i = 0; i < g->get_num_args(); ++i) { + lits.push_back(mk_literal(g->get_arg(i))); + } + literal result = mk_aux_literal(f); + for (unsigned i = 0; i < lits.size(); ++i) { + clause cls(mgr); + cls.m_lits.push_back(~result); + cls.m_weights.push_back(one); + cls.m_lits.push_back(lits[i]); + cls.m_weights.push_back(one); + cls.m_k = one; + cls.m_eq = false; + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); + lits[i].neg(); + } + lits.push_back(result); + clause cls(mgr); + cls.m_lits.append(lits); + pad(cls.m_weights, lits.size(), one); + cls.m_k = one; + cls.m_eq = false; + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); + return result; + } + if (m.is_or(f)) { + literal_vector lits; + app* g = to_app(f); + for (unsigned i = 0; i < g->get_num_args(); ++i) { + lits.push_back(mk_literal(g->get_arg(i))); + } + literal result = mk_aux_literal(f); + for (unsigned i = 0; i < lits.size(); ++i) { + clause cls(mgr); + cls.m_lits.push_back(result); + cls.m_weights.push_back(one); + cls.m_lits.push_back(~lits[i]); + cls.m_weights.push_back(one); + cls.m_k = one; + cls.m_eq = false; + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); + } + lits.push_back(~result); + clause cls(mgr); + cls.m_lits.append(lits); + pad(cls.m_weights, lits.size(), one); + cls.m_k = one; + cls.m_eq = false; + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); + return result; + } + expr* x, *y; + if ((m.is_eq(f, x, y) && m.is_bool(x)) || m.is_iff(f, x, y)) { + literal a = mk_literal(x); + literal b = mk_literal(y); + literal result = mk_aux_literal(f); + clause cls(mgr); + cls.m_lits.push_back(~result); + cls.m_lits.push_back(~a); + cls.m_lits.push_back(b); + pad(cls.m_weights, 3, one); + cls.m_k = one; + cls.m_eq = false; + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); // actually, the clause that defines f + cls.m_lits[0] = ~result; + cls.m_lits[1] = a; + cls.m_lits[2] = ~b; + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); + cls.m_lits[0] = result; + cls.m_lits[1] = a; + cls.m_lits[2] = b; + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); + cls.m_lits[0] = result; + cls.m_lits[1] = ~a; + cls.m_lits[2] = ~b; + m_clauses.push_back(cls); + m_orig_clauses.push_back(f); + return result; + } + if (pb.is_ge(f)) { + + } + IF_VERBOSE(0, verbose_stream() << "not handled: " << mk_pp(f, m) << "\n";); + return mk_aux_literal(f); + } + + bool compile_clause(expr* _f, clause& cls) { + expr_ref tmp(m); + m_rewrite(_f, tmp); + if (!is_app(tmp)) return false; + app* f = to_app(tmp); + expr* f2; + unsigned sz = f->get_num_args(); + expr* const* args = f->get_args(); + literal lit; + rational coeff, k; + if (m.is_not(f, f2) && pb.is_ge(f2)) { + // ~(ax+by >= k) + // <=> + // ax + by < k + // <=> + // -ax - by >= -k + 1 + // <=> + // a(1-x) + b(1-y) >= -k + a + b + 1 + sz = to_app(f2)->get_num_args(); + args = to_app(f2)->get_args(); + k = pb.get_k(f2); + SASSERT(k.is_int()); + k.neg(); + k += rational::one(); + expr_ref_vector args(m); + vector coeffs; + for (unsigned i = 0; i < sz; ++i) { + args.push_back(m.mk_not(to_app(f2)->get_arg(i))); + coeffs.push_back(pb.get_coeff(f2, i)); + k += pb.get_coeff(f2, i); + } + tmp = pb.mk_ge(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k); + return compile_clause(tmp, cls); + } + else if (pb.is_ge(f) || pb.is_eq(f)) { + k = pb.get_k(f); + SASSERT(k.is_int()); + cls.m_k = k.to_mpq().numerator(); + for (unsigned i = 0; i < sz; ++i) { + coeff = pb.get_coeff(f, i); + SASSERT(coeff.is_int()); + lit = mk_literal(args[i]); + if (lit == null_literal) return false; + if (lit == false_literal) continue; + if (lit == true_literal) { + cls.m_k -= coeff.to_mpq().numerator(); + continue; + } + cls.m_lits.push_back(lit); + cls.m_weights.push_back(coeff.to_mpq().numerator()); + if (get_value(lit)) { + cls.m_value += coeff.to_mpq().numerator(); + } + } + cls.m_eq = pb.is_eq(f); + } + else if (m.is_or(f)) { + for (unsigned i = 0; i < sz; ++i) { + lit = mk_literal(args[i]); + if (lit == null_literal) return false; + if (lit == false_literal) continue; + if (lit == true_literal) return false; + cls.m_lits.push_back(lit); + cls.m_weights.push_back(mpz(1)); + if (get_value(lit)) { + cls.m_value += mpz(1); + } + } + cls.m_eq = false; + cls.m_k = mpz(1); + } + else if (m.is_true(f)) { + return false; + } + else { + lit = mk_literal(f); + if (lit == null_literal) return false; + SASSERT(lit != false_literal && lit != true_literal); + cls.m_lits.push_back(lit); + cls.m_weights.push_back(mpz(1)); + cls.m_eq = true; + cls.m_k = mpz(1); + } + return true; + } + + }; + + pb_sls::pb_sls(ast_manager& m) { + m_imp = alloc(imp, m); + } + pb_sls::~pb_sls() { + dealloc(m_imp); + } + void pb_sls::add(expr* f) { + m_imp->add(f); + } + void pb_sls::add(expr* f, rational const& w) { + m_imp->add(f, w); + } + void pb_sls::set_model(model_ref& mdl) { + m_imp->set_model(mdl); + } + lbool pb_sls::operator()() { + return (*m_imp)(); + } + void pb_sls::set_cancel(bool f) { + m_imp->set_cancel(f); + } + void pb_sls::collect_statistics(statistics& st) const { + m_imp->collect_statistics(st); + } + void pb_sls::get_model(model_ref& mdl) { + m_imp->get_model(mdl); + } + void pb_sls::reset() { + m_imp->reset(); + } + bool pb_sls::soft_holds(unsigned index) { + return m_imp->soft_holds(index); + } + void pb_sls::updt_params(params_ref& p) { + m_imp->updt_params(p); + } + +} diff --git a/src/opt/pb_sls.h b/src/opt/pb_sls.h new file mode 100644 index 000000000..655d04b45 --- /dev/null +++ b/src/opt/pb_sls.h @@ -0,0 +1,51 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + pb_sls.h + +Abstract: + + SLS for PB optimization. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-03-18 + +Notes: + +--*/ +#ifndef PB_SLS_H_ +#define PB_SLS_H_ + +#include "pb_decl_plugin.h" +#include "model.h" +#include "lbool.h" +#include "params.h" +#include "statistics.h" + +namespace smt { + + class pb_sls { + struct imp; + imp* m_imp; + public: + pb_sls(ast_manager& m); + ~pb_sls(); + void add(expr* f); + void add(expr* f, rational const& w); + bool soft_holds(unsigned index); + void set_model(model_ref& mdl); + lbool operator()(); + void set_cancel(bool f); + void collect_statistics(::statistics& st) const; + void get_model(model_ref& mdl); + void updt_params(params_ref& p); + void reset(); + }; + + +}; + +#endif diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp new file mode 100644 index 000000000..5f7f76f25 --- /dev/null +++ b/src/opt/wmax.cpp @@ -0,0 +1,83 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + wmax.cpp + +Abstract: + + Theory based MaxSAT. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-17 + +Notes: + +--*/ +#include "wmax.h" +#include "uint_set.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" +#include "smt_theory.h" +#include "smt_context.h" +#include "theory_wmaxsat.h" +#include "opt_context.h" + +namespace opt { + // ---------------------------------------------------------- + // weighted max-sat using a custom theory solver for max-sat. + // NB. it is quite similar to pseudo-Boolean propagation. + + + class wmax : public maxsmt_solver_base { + public: + wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): + maxsmt_solver_base(c, ws, soft) {} + virtual ~wmax() {} + + lbool operator()() { + TRACE("opt", tout << "weighted maxsat\n";); + scoped_ensure_theory wth(*this); + lbool is_sat = l_true; + bool was_sat = false; + for (unsigned i = 0; i < m_soft.size(); ++i) { + wth().assert_weighted(m_soft[i], m_weights[i]); + } + while (l_true == is_sat) { + is_sat = s().check_sat(0,0); + if (m_cancel) { + is_sat = l_undef; + } + if (is_sat == l_true) { + if (wth().is_optimal()) { + m_upper = wth().get_min_cost(); + s().get_model(m_model); + } + expr_ref fml = wth().mk_block(); + s().assert_expr(fml); + was_sat = true; + } + trace_bounds("wmax"); + } + if (was_sat) { + wth().get_assignment(m_assignment); + } + if (is_sat == l_false && was_sat) { + is_sat = l_true; + } + m_upper = wth().get_min_cost(); + if (is_sat == l_true) { + m_lower = m_upper; + } + TRACE("opt", tout << "min cost: " << m_upper << "\n";); + return is_sat; + } + }; + + maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { + return alloc(wmax, c, ws, soft); + } + +} diff --git a/src/opt/wmax.h b/src/opt/wmax.h new file mode 100644 index 000000000..094bb8644 --- /dev/null +++ b/src/opt/wmax.h @@ -0,0 +1,29 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + wmax.h + +Abstract: + + Theory Solver based MaxSAT. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-4-17 + +Notes: + +--*/ + +#ifndef WMAX_H_ +#define WMAX_H_ + +#include "maxsmt.h" + +namespace opt { + maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); + +} +#endif diff --git a/src/parsers/smt/smtlib.cpp b/src/parsers/smt/smtlib.cpp index d53d3cafa..b743b5b0f 100644 --- a/src/parsers/smt/smtlib.cpp +++ b/src/parsers/smt/smtlib.cpp @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #include"smtlib.h" #include"ast_pp.h" #include"ast_smt2_pp.h" diff --git a/src/parsers/smt/smtlib.h b/src/parsers/smt/smtlib.h index e7d4d3a5f..62799ea25 100644 --- a/src/parsers/smt/smtlib.h +++ b/src/parsers/smt/smtlib.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SMTLIB_H_ -#define _SMTLIB_H_ +#ifndef SMTLIB_H_ +#define SMTLIB_H_ #include "ast.h" #include "symbol_table.h" diff --git a/src/parsers/smt/smtlib_solver.h b/src/parsers/smt/smtlib_solver.h index 4e9815d38..fce5ff67f 100644 --- a/src/parsers/smt/smtlib_solver.h +++ b/src/parsers/smt/smtlib_solver.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SMTLIB_SOLVER_H_ -#define _SMTLIB_SOLVER_H_ +#ifndef SMTLIB_SOLVER_H_ +#define SMTLIB_SOLVER_H_ #include"smtparser.h" #include"context_params.h" diff --git a/src/parsers/smt/smtparser.h b/src/parsers/smt/smtparser.h index c63f92664..9a09d1f4f 100644 --- a/src/parsers/smt/smtparser.h +++ b/src/parsers/smt/smtparser.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SMT_PARSER_H_ -#define _SMT_PARSER_H_ +#ifndef SMT_PARSER_H_ +#define SMT_PARSER_H_ #include #include"ast.h" diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 785f578f3..aa38db85f 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -387,6 +387,7 @@ namespace smt2 { void check_keyword(char const * msg) { if (!curr_is_keyword()) throw parser_exception(msg); } void check_string(char const * msg) { if (!curr_is_string()) throw parser_exception(msg); } void check_int(char const * msg) { if (!curr_is_int()) throw parser_exception(msg); } + void check_int_or_float(char const * msg) { if (!curr_is_int() || !curr_is_float()) throw parser_exception(msg); } void check_float(char const * msg) { if (!curr_is_float()) throw parser_exception(msg); } void error(unsigned line, unsigned pos, char const * msg) { @@ -821,10 +822,10 @@ namespace smt2 { check_rparen("invalid datatype declaration"); unsigned sz = new_dt_decls.size(); if (sz == 0) { - m_ctx.print_success(); - next(); + m_ctx.print_success(); + next(); return; - } + } else if (sz == 1) { symbol missing; if (new_dt_decls[0]->has_missing_refs(missing)) { @@ -867,7 +868,7 @@ namespace smt2 { TRACE("declare_datatypes", tout << "i: " << i << " new_dt_decls.size(): " << sz << "\n"; for (unsigned i = 0; i < sz; i++) tout << new_dt_decls[i]->get_name() << "\n";); m_ctx.print_success(); - next(); + next(); } void name_expr(expr * n, symbol const & s) { @@ -1788,9 +1789,9 @@ namespace smt2 { m_ctx.insert(decl); next(); check_rparen("invalid sort declaration, ')' expected"); - } - m_ctx.print_success(); - next(); + } + m_ctx.print_success(); + next(); } void parse_define_sort() { @@ -1810,7 +1811,7 @@ namespace smt2 { m_ctx.insert(decl); check_rparen("invalid sort definition, ')' expected"); m_ctx.print_success(); - next(); + next(); } void parse_define_fun() { @@ -1839,7 +1840,7 @@ namespace smt2 { SASSERT(num_vars == m_num_bindings); m_num_bindings = 0; m_ctx.print_success(); - next(); + next(); } void parse_define_const() { @@ -1859,7 +1860,7 @@ namespace smt2 { expr_stack().pop_back(); sort_stack().pop_back(); m_ctx.print_success(); - next(); + next(); } void parse_declare_fun() { @@ -1878,7 +1879,7 @@ namespace smt2 { m_ctx.insert(f); check_rparen("invalid function declaration, ')' expected"); m_ctx.print_success(); - next(); + next(); } void parse_declare_const() { @@ -1898,7 +1899,7 @@ namespace smt2 { m_ctx.insert(c); check_rparen("invalid constant declaration, ')' expected"); m_ctx.print_success(); - next(); + next(); } unsigned parse_opt_unsigned(unsigned def) { @@ -1927,7 +1928,7 @@ namespace smt2 { m_ctx.push(num); check_rparen("invalid push command, ')' expected"); m_ctx.print_success(); - next(); + next(); } void parse_pop() { @@ -1938,7 +1939,7 @@ namespace smt2 { m_ctx.pop(num); check_rparen("invalid pop command, ')' expected"); m_ctx.print_success(); - next(); + next(); TRACE("after_pop", tout << "expr_stack.size: " << expr_stack().size() << "\n"; m_ctx.dump_assertions(tout);); } @@ -1974,7 +1975,7 @@ namespace smt2 { expr_stack().pop_back(); check_rparen("invalid assert command, ')' expected"); m_ctx.print_success(); - next(); + next(); } void parse_check_sat() { @@ -2061,7 +2062,7 @@ namespace smt2 { } m_ctx.regular_stream() << ")" << std::endl; expr_stack().shrink(spos); - next(); + next(); } void parse_reset() { @@ -2072,7 +2073,7 @@ namespace smt2 { m_ctx.reset(); reset(); m_ctx.print_success(); - next(); + next(); } void parse_option_value() { diff --git a/src/parsers/smt2/smt2parser.h b/src/parsers/smt2/smt2parser.h index d230e5ae0..5b4384917 100644 --- a/src/parsers/smt2/smt2parser.h +++ b/src/parsers/smt2/smt2parser.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SMT2_PARSER_H_ -#define _SMT2_PARSER_H_ +#ifndef SMT2_PARSER_H_ +#define SMT2_PARSER_H_ #include"cmd_context.h" diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index f653cbb25..18d40d48f 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -92,7 +92,7 @@ namespace smt2 { scanner::token scanner::read_symbol_core() { while (true) { char c = curr(); - char n = m_normalized[static_cast(c)]; + signed char n = m_normalized[static_cast(c)]; if (n == 'a' || n == '0' || n == '-') { m_string.push_back(c); next(); @@ -257,7 +257,7 @@ namespace smt2 { m_smtlib2_compliant = ctx.params().m_smtlib2_compliant; for (int i = 0; i < 256; ++i) { - m_normalized[i] = (char) i; + m_normalized[i] = (signed char) i; } m_normalized[static_cast('\t')] = ' '; m_normalized[static_cast('\r')] = ' '; @@ -295,7 +295,7 @@ namespace smt2 { scanner::token scanner::scan() { while (true) { - char c = curr(); + signed char c = curr(); m_pos = m_spos; switch (m_normalized[(unsigned char) c]) { case ' ': diff --git a/src/parsers/smt2/smt2scanner.h b/src/parsers/smt2/smt2scanner.h index 631c71f17..8313c24df 100644 --- a/src/parsers/smt2/smt2scanner.h +++ b/src/parsers/smt2/smt2scanner.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SMT2SCANNER_H_ -#define _SMT2SCANNER_H_ +#ifndef SMT2SCANNER_H_ +#define SMT2SCANNER_H_ #include #include"symbol.h" @@ -42,7 +42,7 @@ namespace smt2 { rational m_number; unsigned m_bv_size; // end of data - char m_normalized[256]; + signed char m_normalized[256]; #define SCANNER_BUFFER_SIZE 1024 char m_buffer[SCANNER_BUFFER_SIZE]; unsigned m_bpos; @@ -105,5 +105,5 @@ namespace smt2 { }; -#endif /* _SCANNER_H_ */ +#endif /* SCANNER_H_ */ diff --git a/src/parsers/util/cost_parser.h b/src/parsers/util/cost_parser.h index bd0b0b86a..37e9ea3a1 100644 --- a/src/parsers/util/cost_parser.h +++ b/src/parsers/util/cost_parser.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _COST_PARSER_H_ -#define _COST_PARSER_H_ +#ifndef COST_PARSER_H_ +#define COST_PARSER_H_ #include"simple_parser.h" #include"arith_decl_plugin.h" @@ -35,5 +35,5 @@ public: void reset_vars(); }; -#endif /* _COST_PARSER_H_ */ +#endif /* COST_PARSER_H_ */ diff --git a/src/parsers/util/pattern_validation.h b/src/parsers/util/pattern_validation.h index 99024097e..6c2ad73b7 100644 --- a/src/parsers/util/pattern_validation.h +++ b/src/parsers/util/pattern_validation.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _PATTERN_VALIDATION_H_ -#define _PATTERN_VALIDATION_H_ +#ifndef PATTERN_VALIDATION_H_ +#define PATTERN_VALIDATION_H_ #include"ast.h" #include"uint_set.h" @@ -39,5 +39,5 @@ public: bool operator()(unsigned num_new_bindings, expr * n) { return operator()(UINT_MAX, num_new_bindings, n); } }; -#endif /* _PATTERN_VALIDATION_H_ */ +#endif /* PATTERN_VALIDATION_H_ */ diff --git a/src/parsers/util/scanner.h b/src/parsers/util/scanner.h index c93c7a82d..f434da164 100644 --- a/src/parsers/util/scanner.h +++ b/src/parsers/util/scanner.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SCANNER_H_ -#define _SCANNER_H_ +#ifndef SCANNER_H_ +#define SCANNER_H_ #include"ast.h" @@ -88,5 +88,5 @@ private: bool state_ok(); }; -#endif /* _SCANNER_H_ */ +#endif /* SCANNER_H_ */ diff --git a/src/parsers/util/simple_parser.h b/src/parsers/util/simple_parser.h index ef68b0b27..8589f140b 100644 --- a/src/parsers/util/simple_parser.h +++ b/src/parsers/util/simple_parser.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SIMPLE_PARSER_H_ -#define _SIMPLE_PARSER_H_ +#ifndef SIMPLE_PARSER_H_ +#define SIMPLE_PARSER_H_ #include"ast.h" #include"map.h" @@ -60,5 +60,5 @@ public: virtual expr * parse_float(rational const & r) { throw parser_error(); } }; -#endif /* _SIMPLE_PARSER_H_ */ +#endif /* SIMPLE_PARSER_H_ */ diff --git a/src/qe/nlarith_util.cpp b/src/qe/nlarith_util.cpp index c555b71f1..b7c1aef5d 100644 --- a/src/qe/nlarith_util.cpp +++ b/src/qe/nlarith_util.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "ast.h" #include "nlarith_util.h" #include "arith_decl_plugin.h" diff --git a/src/qe/nlarith_util.h b/src/qe/nlarith_util.h index a43ad2e18..5854088ab 100644 --- a/src/qe/nlarith_util.h +++ b/src/qe/nlarith_util.h @@ -17,8 +17,8 @@ Author: Notes: --*/ -#ifndef _NLARITH_UTIL_H_ -#define _NLARITH_UTIL_H_ +#ifndef NLARITH_UTIL_H_ +#define NLARITH_UTIL_H_ #include "ast.h" #include "lbool.h" diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index d229d8735..34b697a94 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -81,7 +81,7 @@ namespace qe { ptr_vector todo; ptr_vector conjs_closed, conjs_mixed, conjs_open; - qe::flatten_and(fml, conjs); + flatten_and(fml, conjs); for (unsigned i = 0; i < conjs.size(); ++i) { todo.push_back(conjs[i].get()); @@ -306,7 +306,7 @@ namespace qe { // conj_enum conj_enum::conj_enum(ast_manager& m, expr* e): m(m), m_conjs(m) { - qe::flatten_and(e, m_conjs); + flatten_and(e, m_conjs); } void conj_enum::extract_equalities(expr_ref_vector& eqs) { @@ -1451,7 +1451,7 @@ namespace qe { if (assumption) m_solver.assert_expr(assumption); bool is_sat = false; lbool res = l_true; - while (true) { + while (res == l_true) { res = m_solver.check(); if (res == l_true) { is_sat = true; @@ -2277,17 +2277,14 @@ namespace qe { void expr_quant_elim::instantiate_expr(expr_ref_vector& bound, expr_ref& fml) { - ptr_vector sorts; - get_free_vars(fml, sorts); - if (!sorts.empty()) { + expr_free_vars fv; + fv(fml); + fv.set_default_sort(m.mk_bool_sort()); + if (!fv.empty()) { expr_ref tmp(m); - for (unsigned i = sorts.size(); i > 0;) { + for (unsigned i = fv.size(); i > 0;) { --i; - sort* s = sorts[i]; - if (!s) { - s = m.mk_bool_sort(); - } - bound.push_back(m.mk_fresh_const("bound", s)); + bound.push_back(m.mk_fresh_const("bound", fv[i])); } var_subst subst(m); subst(fml, bound.size(), bound.c_ptr(), tmp); @@ -2438,7 +2435,6 @@ namespace qe { cache_result(q, q, 0); return; } - ast_manager& m = m_manager; quantifier_ref new_q(m); expr * new_body = 0; @@ -2464,7 +2460,6 @@ namespace qe { } void expr_quant_elim_star1::reduce_with_assumption(expr* ctx, expr* fml, expr_ref& result) { - ast_manager& m = m_manager; proof_ref pr(m); m_assumption = ctx; (*this)(fml, result, pr); diff --git a/src/qe/qe.h b/src/qe/qe.h index 0fd2ff60c..7a3337887 100644 --- a/src/qe/qe.h +++ b/src/qe/qe.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef __QE_H__ -#define __QE_H__ +#ifndef QE_H_ +#define QE_H_ #include "ast.h" #include "smt_params.h" diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 360c2ac8c..bc4a824d1 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -20,6 +20,7 @@ Revision History: #include "qe_arith.h" #include "qe_util.h" +#include "ast_util.h" #include "arith_decl_plugin.h" #include "ast_pp.h" #include "th_rewriter.h" @@ -299,7 +300,7 @@ namespace qe { ast_manager& m = vars.get_manager(); arith_project_util ap(m); expr_ref_vector lits(m); - qe::flatten_and(fml, lits); + flatten_and(fml, lits); return ap(model, vars, lits); } diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index c8f7e8b8d..16a89794f 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -1,6 +1,12 @@ -#ifndef __QE_ARITH_H_ -#define __QE_ARITH_H_ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + +#ifndef QE_ARITH_H_ +#define QE_ARITH_H_ #include "model.h" diff --git a/src/qe/qe_arith_plugin.cpp b/src/qe/qe_arith_plugin.cpp index 83c7b79d8..75cba61fe 100644 --- a/src/qe/qe_arith_plugin.cpp +++ b/src/qe/qe_arith_plugin.cpp @@ -2049,6 +2049,7 @@ public: // z < d expr* z_lt_d = m_util.m_arith.mk_le(z, m_util.m_arith.mk_numeral(d-rational(1), true)); m_ctx.add_constraint(false, z_lt_d); + TRACE("qe", tout << mk_pp(z_lt_d, m) << "\n";); // result <- result & z <= d - 1 SASSERT(!abs(d).is_one()); @@ -2062,9 +2063,11 @@ public: t1 = m_util.mk_sub(x, z); m_util.mk_divides(d, t1, new_atom); m_ctx.add_constraint(false, new_atom); + TRACE("qe", tout << mk_pp(new_atom, m) << "\n";); // (c | ax + t <-> c | az + t) for each divisor. mk_div_equivs(bounds, z, result); + TRACE("qe", tout << mk_pp(result, m) << "\n";); // update x_t to map x |-> dx + z x_t.set_term(z); diff --git a/src/qe/qe_array_plugin.cpp b/src/qe/qe_array_plugin.cpp index c9de1d745..e7cbe65b9 100644 --- a/src/qe/qe_array_plugin.cpp +++ b/src/qe/qe_array_plugin.cpp @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #include "qe.h" #include "array_decl_plugin.h" #include "expr_safe_replace.h" diff --git a/src/qe/qe_cmd.cpp b/src/qe/qe_cmd.cpp index 6f001c9da..9144c708c 100644 --- a/src/qe/qe_cmd.cpp +++ b/src/qe/qe_cmd.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "qe_cmd.h" #include "qe.h" #include "cmd_context.h" diff --git a/src/qe/qe_cmd.h b/src/qe/qe_cmd.h index 3012e8eba..97cd34753 100644 --- a/src/qe/qe_cmd.h +++ b/src/qe/qe_cmd.h @@ -15,8 +15,8 @@ Author: Notes: --*/ -#ifndef _QE_CMD_H_ -#define _QE_CMD_H_ +#ifndef QE_CMD_H_ +#define QE_CMD_H_ class cmd_context; diff --git a/src/qe/qe_datatype_plugin.cpp b/src/qe/qe_datatype_plugin.cpp index 9b77de42a..088d2252d 100644 --- a/src/qe/qe_datatype_plugin.cpp +++ b/src/qe/qe_datatype_plugin.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + // --------------------- // datatypes // Quantifier elimination routine for recursive data-types. diff --git a/src/qe/qe_dl_plugin.cpp b/src/qe/qe_dl_plugin.cpp index a93301b4f..e04f4cbde 100644 --- a/src/qe/qe_dl_plugin.cpp +++ b/src/qe/qe_dl_plugin.cpp @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #include "qe.h" #include "expr_safe_replace.h" #include "dl_decl_plugin.h" diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 130673527..f045e406e 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -30,6 +30,7 @@ Revision History: #include "bool_rewriter.h" #include "var_subst.h" #include "uint_set.h" +#include "ast_util.h" #include "qe_util.h" #include "th_rewriter.h" #include "for_each_expr.h" @@ -238,67 +239,95 @@ namespace eq { } } - bool solve_arith_core(app * lhs, expr * rhs, expr * eq, ptr_vector& vs, expr_ref_vector& ts) { - SASSERT(a.is_add(lhs)); - bool is_int = a.is_int(lhs); - expr * a1, *v; - expr_ref def(m); - rational a_val; - unsigned num = lhs->get_num_args(); - unsigned i; - for (i = 0; i < num; i++) { - expr * arg = lhs->get_arg(i); - if (is_variable(arg)) { - a_val = rational(1); - v = arg; - break; - } - else if (a.is_mul(arg, a1, v) && - is_variable(v) && - a.is_numeral(a1, a_val) && - !a_val.is_zero() && - (!is_int || a_val.is_minus_one())) { - break; + bool is_invertible_const(bool is_int, expr* x, rational& a_val) { + expr* y; + if (a.is_uminus(x, y) && is_invertible_const(is_int, y, a_val)) { + a_val.neg(); + return true; + } + else if (a.is_numeral(x, a_val) && !a_val.is_zero()) { + if (!is_int || a_val.is_one() || a_val.is_minus_one()) { + return true; } } - if (i == num) - return false; - vs.push_back(to_var(v)); - expr_ref inv_a(m); - if (!a_val.is_one()) { - inv_a = a.mk_numeral(rational(1)/a_val, is_int); - rhs = a.mk_mul(inv_a, rhs); - } - - ptr_buffer other_args; - for (unsigned j = 0; j < num; j++) { - if (i != j) { - if (inv_a) - other_args.push_back(a.mk_mul(inv_a, lhs->get_arg(j))); - else - other_args.push_back(lhs->get_arg(j)); - } - } - switch (other_args.size()) { - case 0: - def = rhs; - break; - case 1: - def = a.mk_sub(rhs, other_args[0]); - break; - default: - def = a.mk_sub(rhs, a.mk_add(other_args.size(), other_args.c_ptr())); - break; - } - ts.push_back(def); - return true; + return false; } + bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { + if (is_variable(arg)) { + a_val = rational(1); + return true; + } + expr* x, *y; + if (a.is_mul(arg, x, y)) { + if (is_variable(x) && is_invertible_const(is_int, y, a_val)) { + arg = x; + return true; + } + if (is_variable(y) && is_invertible_const(is_int, x, a_val)) { + arg = y; + return true; + } + } + return false; + } + + typedef std::pair signed_expr; + + expr_ref solve_arith(bool is_int, rational const& r, bool sign, svector const& exprs) { + expr_ref_vector result(m); + for (unsigned i = 0; i < exprs.size(); ++i) { + bool sign2 = exprs[i].first; + expr* e = exprs[i].second; + rational r2(r); + if (sign == sign2) { + r2.neg(); + } + if (!r2.is_one()) { + result.push_back(a.mk_mul(a.mk_numeral(r2, is_int), e)); + } + else { + result.push_back(e); + } + } + return expr_ref(a.mk_add(result.size(), result.c_ptr()), m); + } + + bool solve_arith(expr* lhs, expr* rhs, ptr_vector& vs, expr_ref_vector& ts) { + if (!a.is_int(lhs) && !a.is_real(rhs)) { + return false; + } + rational a_val; + bool is_int = a.is_int(lhs); + svector todo, done; + todo.push_back(std::make_pair(true, lhs)); + todo.push_back(std::make_pair(false, rhs)); + while (!todo.empty()) { + expr* e = todo.back().second; + bool sign = todo.back().first; + todo.pop_back(); + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + todo.push_back(std::make_pair(sign, to_app(e)->get_arg(i))); + } + } + else if (is_invertible_mul(is_int, e, a_val)) { + done.append(todo); + vs.push_back(to_var(e)); + a_val = rational(1)/a_val; + ts.push_back(solve_arith(is_int, a_val, sign, done)); + TRACE("qe_lite", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << mk_pp(e, m) << " := " << mk_pp(ts.back(), m) << "\n";); + return true; + } + else { + done.push_back(std::make_pair(sign, e)); + } + } + return false; + } bool arith_solve(expr * lhs, expr * rhs, expr * eq, ptr_vector& vs, expr_ref_vector& ts) { - return - (a.is_add(lhs) && solve_arith_core(to_app(lhs), rhs, eq, vs, ts)) || - (a.is_add(rhs) && solve_arith_core(to_app(rhs), lhs, eq, vs, ts)); + return solve_arith(lhs, rhs, vs, ts); } bool trivial_solve(expr* lhs, expr* rhs, expr* eq, ptr_vector& vs, expr_ref_vector& ts) { @@ -695,7 +724,7 @@ namespace eq { m_subst(r, m_subst_map.size(), m_subst_map.c_ptr(), new_r); m_rewriter(new_r); conjs.reset(); - qe::flatten_and(new_r, conjs); + flatten_and(new_r, conjs); reduced = true; } } @@ -1026,7 +1055,7 @@ namespace fm { arith_util m_util; constraints m_constraints; expr_ref_vector m_bvar2expr; - char_vector m_bvar2sign; + signed_char_vector m_bvar2sign; obj_map m_expr2bvar; char_vector m_is_int; char_vector m_forbidden; @@ -2035,7 +2064,7 @@ namespace fm { switch (m_bvar2sign[p]) { case 0: new_lits.push_back(lit); - break; + break; case -1: if (!sign(lit)) tautology = true; @@ -2386,7 +2415,7 @@ public: void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml) { expr_ref_vector disjs(m); - qe::flatten_or(fml, disjs); + flatten_or(fml, disjs); for (unsigned i = 0; i < disjs.size(); ++i) { expr_ref_vector conjs(m); conjs.push_back(disjs[i].get()); @@ -2399,7 +2428,7 @@ public: void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& fmls) { - qe::flatten_and(fmls); + flatten_and(fmls); unsigned index; if (has_unique_non_ground(fmls, index)) { expr_ref fml(m); diff --git a/src/qe/qe_lite.h b/src/qe/qe_lite.h index 7d9239fa7..aa81780ba 100644 --- a/src/qe/qe_lite.h +++ b/src/qe/qe_lite.h @@ -18,8 +18,8 @@ Revision History: --*/ -#ifndef __QE_LITE_H__ -#define __QE_LITE_H__ +#ifndef QE_LITE_H_ +#define QE_LITE_H_ #include "ast.h" #include "uint_set.h" diff --git a/src/qe/qe_sat_tactic.cpp b/src/qe/qe_sat_tactic.cpp index 2be32c02d..35be20660 100644 --- a/src/qe/qe_sat_tactic.cpp +++ b/src/qe/qe_sat_tactic.cpp @@ -67,6 +67,7 @@ namespace qe { bool m_ctx_simplify_local_param; vector m_vars; ptr_vector m_solvers; + vector m_fparamv; smt::kernel m_solver; expr_ref m_fml; expr_ref_vector m_Ms; @@ -229,9 +230,7 @@ namespace qe { } virtual ~sat_tactic() { - for (unsigned i = 0; i < m_solvers.size(); ++i) { - dealloc(m_solvers[i]); - } + reset(); } virtual void set_cancel(bool f) { @@ -323,12 +322,14 @@ namespace qe { unsigned num_alternations() const { return m_vars.size(); } void init_Ms() { - for (unsigned i = 0; i < num_alternations(); ++i) { - m_Ms.push_back(m.mk_true()); - m_solvers.push_back(alloc(smt::kernel, m, m_fparams, m_params)); + for (unsigned i = 0; i <= num_alternations(); ++i) { + m_fparamv.push_back(m_fparams); } - m_Ms.push_back(m_fml); - m_solvers.push_back(alloc(smt::kernel, m, m_fparams, m_params)); + for (unsigned i = 0; i <= num_alternations(); ++i) { + m_Ms.push_back(m.mk_true()); + m_solvers.push_back(alloc(smt::kernel, m, m_fparamv[i], m_params)); + } + m_Ms[m_Ms.size()-1] = m_fml; m_solvers.back()->assert_expr(m_fml); } @@ -339,8 +340,13 @@ namespace qe { smt::kernel& solver(unsigned i) { return *m_solvers[i]; } void reset() { + for (unsigned i = 0; i < m_solvers.size(); ++i) { + dealloc(m_solvers[i]); + } m_fml = 0; m_Ms.reset(); + m_fparamv.reset(); + m_solvers.reset(); m_vars.reset(); } @@ -359,7 +365,7 @@ namespace qe { void extract_alt_form(expr* fml) { quantifier_hoister hoist(m); expr_ref result(m); - bool is_fa; + bool is_fa = false; unsigned parity = 0; m_fml = fml; while (true) { @@ -399,7 +405,7 @@ namespace qe { expr_ref qt(unsigned i, expr* ctx, model_ref& model) { model_ref model1; while (true) { - IF_VERBOSE(1, verbose_stream() << "qt " << i << "\n";); + IF_VERBOSE(1, verbose_stream() << "(qt " << i << ")\n";); TRACE("qe", tout << i << " " << mk_pp(ctx, m) << "\n"; display(tout);); @@ -638,7 +644,8 @@ namespace qe { TRACE("qe", tout << mk_pp(_fml, m) << "\n-- context simplify -->\n" << mk_pp(fml, m) << "\n";); } solver_context ctx(*this, idx); - ctx.add_plugin(mk_arith_plugin(ctx, false, m_fparams)); + smt_params fparams(m_fparams); + ctx.add_plugin(mk_arith_plugin(ctx, false, fparams)); ctx.add_plugin(mk_bool_plugin(ctx)); ctx.add_plugin(mk_bv_plugin(ctx)); ctx.init(fml, idx); diff --git a/src/qe/qe_sat_tactic.h b/src/qe/qe_sat_tactic.h index 15228c534..0b276fe8d 100644 --- a/src/qe/qe_sat_tactic.h +++ b/src/qe/qe_sat_tactic.h @@ -19,8 +19,8 @@ Revision History: --*/ -#ifndef __QE_SAT_H__ -#define __QE_SAT_H__ +#ifndef QE_SAT_H_ +#define QE_SAT_H_ #include"tactic.h" diff --git a/src/qe/qe_tactic.h b/src/qe/qe_tactic.h index c49f47795..68860f488 100644 --- a/src/qe/qe_tactic.h +++ b/src/qe/qe_tactic.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _QE_TACTIC_H_ -#define _QE_TACTIC_H_ +#ifndef QE_TACTIC_H_ +#define QE_TACTIC_H_ #include"params.h" class ast_manager; diff --git a/src/qe/qe_util.cpp b/src/qe/qe_util.cpp index 77396ac49..2bcda608f 100644 --- a/src/qe/qe_util.cpp +++ b/src/qe/qe_util.cpp @@ -1,119 +1,13 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "qe_util.h" #include "bool_rewriter.h" namespace qe { - void flatten_and(expr_ref_vector& result) { - ast_manager& m = result.get_manager(); - expr* e1, *e2, *e3; - for (unsigned i = 0; i < result.size(); ++i) { - if (m.is_and(result[i].get())) { - app* a = to_app(result[i].get()); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(a->get_arg(j)); - } - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { - result[i] = e2; - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_or(e1)) { - app* a = to_app(e1); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(m.mk_not(a->get_arg(j))); - } - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) { - result.push_back(e2); - result[i] = m.mk_not(e3); - --i; - } - else if (m.is_true(result[i].get()) || - (m.is_not(result[i].get(), e1) && - m.is_false(e1))) { - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_false(result[i].get()) || - (m.is_not(result[i].get(), e1) && - m.is_true(e1))) { - result.reset(); - result.push_back(m.mk_false()); - return; - } - } - } - - void flatten_and(expr* fml, expr_ref_vector& result) { - SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); - flatten_and(result); - } - - void flatten_or(expr_ref_vector& result) { - ast_manager& m = result.get_manager(); - expr* e1, *e2, *e3; - for (unsigned i = 0; i < result.size(); ++i) { - if (m.is_or(result[i].get())) { - app* a = to_app(result[i].get()); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(a->get_arg(j)); - } - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { - result[i] = e2; - --i; - } - else if (m.is_not(result[i].get(), e1) && m.is_and(e1)) { - app* a = to_app(e1); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(m.mk_not(a->get_arg(j))); - } - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_implies(result[i].get(),e2,e3)) { - result.push_back(e3); - result[i] = m.mk_not(e2); - --i; - } - else if (m.is_false(result[i].get()) || - (m.is_not(result[i].get(), e1) && - m.is_true(e1))) { - result[i] = result.back(); - result.pop_back(); - --i; - } - else if (m.is_true(result[i].get()) || - (m.is_not(result[i].get(), e1) && - m.is_false(e1))) { - result.reset(); - result.push_back(m.mk_true()); - return; - } - } - } - - - void flatten_or(expr* fml, expr_ref_vector& result) { - SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); - flatten_or(result); - } expr_ref mk_and(expr_ref_vector const& fmls) { ast_manager& m = fmls.get_manager(); diff --git a/src/qe/qe_util.h b/src/qe/qe_util.h index f1a99ec6c..ea082d78c 100644 --- a/src/qe/qe_util.h +++ b/src/qe/qe_util.h @@ -16,22 +16,12 @@ Author: Revision History: --*/ -#ifndef _QE_UTIL_H_ -#define _QE_UTIL_H_ +#ifndef QE_UTIL_H_ +#define QE_UTIL_H_ #include "ast.h" namespace qe { - /** - \brief Collect top-level conjunctions and disjunctions. - */ - void flatten_and(expr_ref_vector& result); - - void flatten_and(expr* fml, expr_ref_vector& result); - - void flatten_or(expr_ref_vector& result); - - void flatten_or(expr* fml, expr_ref_vector& result); expr_ref mk_and(expr_ref_vector const& fmls); diff --git a/src/qe/vsubst_tactic.h b/src/qe/vsubst_tactic.h index b64232d60..8c0cb682f 100644 --- a/src/qe/vsubst_tactic.h +++ b/src/qe/vsubst_tactic.h @@ -17,8 +17,8 @@ Notes: --*/ -#ifndef _VSUBST_TACTIC_H_ -#define _VSUBST_TACTIC_H_ +#ifndef VSUBST_TACTIC_H_ +#define VSUBST_TACTIC_H_ #include"params.h" class ast_manager; diff --git a/src/sat/dimacs.h b/src/sat/dimacs.h index 63f0a1ee1..9d7bb0679 100644 --- a/src/sat/dimacs.h +++ b/src/sat/dimacs.h @@ -16,12 +16,12 @@ Author: Revision History: --*/ -#ifndef _DIMACS_H_ -#define _DIMACS_H_ +#ifndef DIMACS_H_ +#define DIMACS_H_ #include"sat_types.h" void parse_dimacs(std::istream & s, sat::solver & solver); -#endif /* _DIMACS_PARSER_H_ */ +#endif /* DIMACS_PARSER_H_ */ diff --git a/src/sat/sat_asymm_branch.h b/src/sat/sat_asymm_branch.h index f10522fab..4ad702c5c 100644 --- a/src/sat/sat_asymm_branch.h +++ b/src/sat/sat_asymm_branch.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_ASYMM_BRANCH_H_ -#define _SAT_ASYMM_BRANCH_H_ +#ifndef SAT_ASYMM_BRANCH_H_ +#define SAT_ASYMM_BRANCH_H_ #include"sat_types.h" #include"statistics.h" diff --git a/src/sat/sat_bceq.cpp b/src/sat/sat_bceq.cpp new file mode 100644 index 000000000..a194cdfd8 --- /dev/null +++ b/src/sat/sat_bceq.cpp @@ -0,0 +1,529 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sat_bceq.cpp + +Abstract: + + Find equivalent literals based on blocked clause decomposition. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-09-27. + + +Revision History: + +--*/ +#include"sat_bceq.h" +#include"sat_solver.h" +#include"trace.h" +#include"bit_vector.h" +#include"map.h" +#include"sat_elim_eqs.h" + +namespace sat { + + void bceq::use_list::init(unsigned num_vars) { + m_clauses.reset(); + m_clauses.resize(2*num_vars); + } + + void bceq::use_list::insert(clause& c) { + unsigned sz = c.size(); + for (unsigned i = 0; i < sz; i++) { + m_clauses[c[i].index()].push_back(&c); + } + } + + void bceq::use_list::erase(clause& c) { + unsigned sz = c.size(); + for (unsigned i = 0; i < sz; i++) { + m_clauses[c[i].index()].erase(&c); + } + } + + ptr_vector& bceq::use_list::get(literal lit) { + return m_clauses[lit.index()]; + } + + bceq::bceq(solver & s): + m_solver(s) { + } + + void bceq::register_clause(clause* cls) { + m_clauses.setx(cls->id(), cls, 0); + } + + void bceq::unregister_clause(clause* cls) { + m_clauses.setx(cls->id(), 0, 0); + } + + void bceq::init() { + m_clauses.reset(); + m_bin_clauses.reset(); + m_L.reset(); + m_R.reset(); + m_L_blits.reset(); + m_R_blits.reset(); + m_bce_use_list.reset(); + clause * const* it = m_solver.begin_clauses(); + clause * const* end = m_solver.end_clauses(); + for (; it != end; ++it) { + clause* cls = *it; + if (!cls->was_removed()) { + m_use_list->insert(*cls); + register_clause(cls); + } + } + bin_clauses bc; + m_solver.collect_bin_clauses(bc, false); // exclude roots. + literal lits[2]; + for (unsigned i = 0; i < bc.size(); ++i) { + lits[0] = bc[i].first; + lits[1] = bc[i].second; + clause* cls = m_solver.m_cls_allocator.mk_clause(2, lits, false); + m_use_list->insert(*cls); + m_bin_clauses.push_back(cls); + register_clause(cls); + } + TRACE("sat", + for (unsigned i = 0; i < m_clauses.size(); ++i) { + clause const* cls = m_clauses[i]; + if (cls) tout << *cls << "\n"; + }); + } + + void bceq::pure_decompose() { + // while F != empty + // pick a clause and variable x in clause. + // get use list U1 of x and U2 of ~x + // assume |U1| >= |U2| + // add U1 to clause set. + for (unsigned i = 0; i < m_clauses.size(); ++i) { + clause* cls = m_clauses[i]; + if (cls) { + SASSERT(i == cls->id()); + pure_decompose((*cls)[0]); + SASSERT(!m_clauses[i]); + } + } + m_L.reverse(); + m_L_blits.reverse(); + } + + void bceq::pure_decompose(literal lit) { + clause_use_list& pos = m_use_list->get(lit); + clause_use_list& neg = m_use_list->get(~lit); + unsigned sz1 = m_L.size(); + unsigned sz2 = m_R.size(); + pure_decompose(pos, m_L); + pure_decompose(neg, m_R); + unsigned delta1 = m_L.size() - sz1; + unsigned delta2 = m_R.size() - sz2; + if (delta1 < delta2) { + m_L_blits.resize(sz1+delta2, ~lit); + m_R_blits.resize(sz2+delta1, lit); + for (unsigned i = 0; i < delta1; ++i) { + std::swap(m_L[sz1 + i], m_R[sz2 + i]); + } + for (unsigned i = delta1; i < delta2; ++i) { + m_L.push_back(m_R[sz2 + i]); + } + m_R.resize(sz2 + delta1); + std::swap(delta1, delta2); + } + else { + m_L_blits.resize(sz1+delta1, lit); + m_R_blits.resize(sz2+delta2, ~lit); + } + TRACE("bceq", tout << lit << " " << "pos: " << delta1 << " " << "neg: " << delta2 << "\n";); + } + + void bceq::pure_decompose(clause_use_list& uses, svector& clauses) { + unsigned sz = uses.size(); + for (unsigned i = 0; i < sz; ++i) { + clause& cls = *uses[i]; + if (!cls.was_removed() && m_clauses[cls.id()]) { + clauses.push_back(&cls); + m_clauses[cls.id()] = 0; + } + } + } + + void bceq::post_decompose() { + m_marked.reset(); + m_marked.resize(2*m_solver.num_vars(), false); + use_list ul; + use_list* save = m_use_list; + m_use_list = &ul; + ul.init(m_solver.num_vars()); + for (unsigned i = 0; i < m_L.size(); ++i) { + ul.insert(*m_L[i]); + } + + // cheap pass: add clauses from R in order + // such that they are blocked with respect to + // predecessors. + m_removed.reset(); + for (unsigned i = 0; i < m_R.size(); ++i) { + literal lit = find_blocked(*m_R[i]); + if (lit != null_literal) { + m_L.push_back(m_R[i]); + m_L_blits.push_back(lit); + ul.insert(*m_R[i]); + m_R[i] = m_R.back(); + m_R_blits[i] = m_R_blits.back(); + m_R.pop_back(); + m_R_blits.pop_back(); + --i; + } + } + // expensive pass: add clauses from R as long + // as BCE produces the empty set of clauses. + m_bce_use_list.init(m_solver.num_vars()); + for (unsigned i = 0; i < m_L.size(); ++i) { + m_bce_use_list.insert(*m_L[i]); + } + for (unsigned i = 0; i < m_R.size(); ++i) { + if (bce(*m_R[i])) { + m_R[i] = m_R.back(); + m_R_blits[i] = m_R_blits.back(); + m_R.pop_back(); + m_R_blits.pop_back(); + --i; + } + } + m_use_list = save; + } + + + // Note: replay blocked clause elimination: + // Suppose C u { c1 } is blocked. + // annotate each clause by blocking literal. + // for new clause c2, check if C u { c2 } is blocked. + // For each c in C record which literal it is blocked. + // (Order the clauses in C by block ordering) + // l | c is blocked, + // -> c2 contains ~l => check if c c2 is blocked + // + bool bceq::bce(clause& cls0) { + IF_VERBOSE(1, verbose_stream() << "bce " << m_L.size() << " " << m_R.size() << " " << cls0 << "\n";); + unsigned_vector& live_clauses = m_live_clauses; + live_clauses.reset(); + m_use_list = &m_bce_use_list; + m_bce_use_list.insert(cls0); + svector& clauses = m_L; + literal_vector& blits = m_L_blits; + clauses.push_back(&cls0); + blits.push_back(null_literal); + bool removed = false; + m_removed.reset(); + for (unsigned i = 0; i < clauses.size(); ++i) { + clause& cls1 = *clauses[i]; + literal lit = find_blocked(cls1); + if (lit == null_literal) { + live_clauses.push_back(i); + } + else { + m_removed.setx(cls1.id(), true, false); + removed = true; + } + } + while (removed) { + removed = false; + //std::cout << live_clauses.size() << " "; + for (unsigned i = 0; i < live_clauses.size(); ++i) { + clause& cls1 = *clauses[live_clauses[i]]; + literal lit = find_blocked(cls1); + if (lit != null_literal) { + m_removed.setx(cls1.id(), true, false); + removed = true; + live_clauses[i] = live_clauses.back(); + live_clauses.pop_back(); + --i; + } + } + } + //std::cout << "\n"; + m_bce_use_list.erase(cls0); + clauses.pop_back(); + blits.pop_back(); + return live_clauses.empty(); + } + + literal bceq::find_blocked(clause const& cls) { + TRACE("bceq", tout << cls << "\n";); + + unsigned sz = cls.size(); + for (unsigned i = 0; i < sz; ++i) { + m_marked[(~cls[i]).index()] = true; + } + literal result = null_literal; + for (unsigned i = 0; i < sz; ++i) { + literal lit = cls[i]; + if (is_blocked(lit)) { + TRACE("bceq", tout << "is blocked " << lit << " : " << cls << "\n";); + result = lit; + break; + } + } + for (unsigned i = 0; i < sz; ++i) { + m_marked[(~cls[i]).index()] = false; + } + return result; + } + + bool bceq::is_blocked(literal lit) const { + clause_use_list& uses = m_use_list->get(~lit); + unsigned sz = uses.size(); + for (unsigned i = 0; i < sz; ++i) { + clause const& cls = *uses[i]; + unsigned sz = cls.size(); + bool is_axiom = m_removed.get(cls.id(), false); + for (unsigned i = 0; !is_axiom && i < sz; ++i) { + is_axiom = m_marked[cls[i].index()] && cls[i] != ~lit; + } + + TRACE("bceq", tout << "resolvent " << lit << " : " << cls << " " << (is_axiom?"axiom":"non-axiom") << "\n";); + if (!is_axiom) { + return false; + } + } + return true; + } + + + void bceq::init_rbits() { + m_rbits.reset(); + for (unsigned i = 0; i < m_solver.num_vars(); ++i) { + uint64 lo = m_rand() + (m_rand() << 16); + uint64 hi = m_rand() + (m_rand() << 16); + m_rbits.push_back(lo + (hi << 32ULL)); + } + } + + void bceq::init_reconstruction_stack() { + m_rstack.reset(); + m_bstack.reset(); + // decomposition already creates a blocked stack in the proper order. + m_rstack.append(m_L); + m_bstack.append(m_L_blits); + } + + uint64 bceq::eval_clause(clause const& cls) const { + uint64 b = 0; + unsigned sz = cls.size(); + for (unsigned i = 0; i < sz; ++i) { + literal lit = cls[i]; + uint64 val = m_rbits[lit.var()]; + if (lit.sign()) { + val = ~val; + } + b |= val; + } + return b; + } + + void bceq::sat_sweep() { + init_rbits(); + init_reconstruction_stack(); + for (unsigned i = 0; i < m_rstack.size(); ++i) { + clause const& cls = *m_rstack[i]; + literal block_lit = m_bstack[i]; + uint64 b = eval_clause(cls); + // v = 0, b = 0 -> v := 1 + // v = 0, b = 1 -> v := 0 + // v = 1, b = 0 -> v := 0 + // v = 1, b = 1 -> v := 1 + m_rbits[block_lit.var()] ^= ~b; + + } + DEBUG_CODE(verify_sweep();); + } + + void bceq::verify_sweep() { + for (unsigned i = 0; i < m_L.size(); ++i) { + uint64 b = eval_clause(*m_L[i]); + SASSERT((~b) == 0); + } + } + + struct u64_hash { unsigned operator()(uint64 u) const { return (unsigned)u; } }; + + struct u64_eq { bool operator()(uint64 u1, uint64 u2) const { return u1 == u2; } }; + + void bceq::extract_partition() { + unsigned num_vars = m_solver.num_vars(); + map table; + union_find<> union_find(m_union_find_ctx); + for (unsigned i = 0; i < num_vars; ++i) { + m_s->mk_var(true, true); + union_find.mk_var(); + } + for (unsigned i = 0; i < m_L.size(); ++i) { + m_s->mk_clause(m_L[i]->size(), m_L[i]->begin()); + } + for (unsigned i = 0; i < num_vars; ++i) { + uint64 val = m_rbits[i]; + unsigned index; + if (table.find(val, index)) { + union_find.merge(i, index); + } + else if (table.find(~val, index)) { + union_find.merge(i, index); + } + else { + table.insert(val, i); + } + } + TRACE("sat", union_find.display(tout);); + + // + // Preliminary version: + // A more appropriate is to walk each pair, + // and refine partition based on SAT results. + // + for (unsigned i = 0; i < num_vars; ++i) { + if (!union_find.is_root(i)) continue; + unsigned v = union_find.next(i); + unsigned last_v = UINT_MAX; + if (!m_solver.was_eliminated(i)) { + last_v = i; + } + while (v != i) { + if (!m_solver.was_eliminated(v)) { + if (last_v != UINT_MAX) { + if (check_equality(v, last_v)) { + // last_v was eliminated. + + } + else { + // TBD: refine partition. + } + } + last_v = v; + } + v = union_find.next(v); + } + } + } + + bool bceq::check_equality(unsigned v1, unsigned v2) { + TRACE("sat", tout << "check: " << v1 << " = " << v2 << "\n";); + uint64 val1 = m_rbits[v1]; + uint64 val2 = m_rbits[v2]; + literal l1 = literal(v1, false); + literal l2 = literal(v2, false); + if (val1 != val2) { + SASSERT(val1 == ~val2); + l2.neg(); + } + if (is_already_equiv(l1, l2)) { + TRACE("sat", tout << "Already equivalent: " << l1 << " " << l2 << "\n";); + return false; + } + + literal lits[2]; + lits[0] = l1; + lits[1] = ~l2; + lbool is_sat = m_s->check(2, lits); + if (is_sat == l_false) { + lits[0] = ~l1; + lits[1] = l2; + is_sat = m_s->check(2, lits); + } + if (is_sat == l_false) { + TRACE("sat", tout << "Found equivalent: " << l1 << " " << l2 << "\n";); + assert_equality(l1, l2); + } + else { + TRACE("sat", tout << "Not equivalent: " << l1 << " " << l2 << "\n";); + // TBD: if is_sat == l_true, then refine partition. + } + return is_sat == l_false; + } + + bool bceq::is_already_equiv(literal l1, literal l2) { + watch_list const& w1 = m_solver.get_wlist(l1); + bool found = false; + for (unsigned i = 0; !found && i < w1.size(); ++i) { + watched const& w = w1[i]; + found = w.is_binary_clause() && w.get_literal() == ~l2; + } + if (!found) return false; + found = false; + watch_list const& w2 = m_solver.get_wlist(~l1); + for (unsigned i = 0; !found && i < w2.size(); ++i) { + watched const& w = w2[i]; + found = w.is_binary_clause() && w.get_literal() == l2; + } + return found; + } + + void bceq::assert_equality(literal l1, literal l2) { + if (l2.sign()) { + l1.neg(); + l2.neg(); + } + literal_vector roots; + bool_var_vector vars; + for (unsigned i = 0; i < m_solver.num_vars(); ++i) { + roots.push_back(literal(i, false)); + } + roots[l2.var()] = l1; + vars.push_back(l2.var()); + elim_eqs elim(m_solver); + IF_VERBOSE(1, + for (unsigned i = 0; i < vars.size(); ++i) { + verbose_stream() << "var: " << vars[i] << " root: " << roots[vars[i]] << "\n"; + }); + elim(roots, vars); + } + + void bceq::cleanup() { + m_solver.del_clauses(m_bin_clauses.begin(), m_bin_clauses.end()); + m_bin_clauses.reset(); + } + + + void bceq::operator()() { + if (!m_solver.m_config.m_bcd) return; + flet _disable_bcd(m_solver.m_config.m_bcd, false); + flet _disable_min(m_solver.m_config.m_minimize_core, false); + flet _disable_opt(m_solver.m_config.m_optimize_model, false); + flet _bound_maxc(m_solver.m_config.m_max_conflicts, 1500); + + use_list ul; + solver s(m_solver.m_params, m_solver.rlimit(), 0); + s.m_config.m_bcd = false; + s.m_config.m_minimize_core = false; + s.m_config.m_optimize_model = false; + s.m_config.m_max_conflicts = 1500; + m_use_list = &ul; + m_s = &s; + ul.init(m_solver.num_vars()); + init(); + pure_decompose(); + post_decompose(); + IF_VERBOSE(1, verbose_stream() << "Decomposed set " << m_L.size() << " rest: " << m_R.size() << "\n";); + + TRACE("sat", + tout << "Decomposed set " << m_L.size() << "\n"; + for (unsigned i = 0; i < m_L.size(); ++i) { + clause const* cls = m_L[i]; + if (cls) tout << *cls << "\n"; + } + tout << "remainder " << m_R.size() << "\n"; + for (unsigned i = 0; i < m_R.size(); ++i) { + clause const* cls = m_R[i]; + if (cls) tout << *cls << "\n"; + } + ); + sat_sweep(); + extract_partition(); + cleanup(); + } +}; diff --git a/src/sat/sat_bceq.h b/src/sat/sat_bceq.h new file mode 100644 index 000000000..c9d01e78b --- /dev/null +++ b/src/sat/sat_bceq.h @@ -0,0 +1,89 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sat_bceq.h + +Abstract: + + Find equivalent literals based on blocked clause decomposition. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-09-27. + +Revision History: + +--*/ +#ifndef SAT_BCEQ_H_ +#define SAT_BCEQ_H_ + +#include"sat_types.h" +#include"union_find.h" + + +namespace sat { + class solver; + + class bceq { + typedef ptr_vector clause_use_list; + class use_list { + vector > m_clauses; + public: + use_list() {} + void init(unsigned num_vars); + void reset() { m_clauses.reset(); } + void erase(clause& c); + void insert(clause& c); + ptr_vector& get(literal lit); + }; + typedef std::pair bin_clause; + typedef svector bin_clauses; + solver & m_solver; + use_list* m_use_list; + use_list m_bce_use_list; + solver* m_s; + random_gen m_rand; + svector m_clauses; + svector m_L; + svector m_R; + literal_vector m_L_blits; + literal_vector m_R_blits; + svector m_bin_clauses; + svector m_rbits; + svector m_rstack; // stack of blocked clauses + literal_vector m_bstack; // stack of blocking literals + svector m_marked; + svector m_removed; // set of clauses removed (not considered in clause set during BCE) + union_find_default_ctx m_union_find_ctx; + unsigned_vector m_live_clauses; + + void init(); + void register_clause(clause* cls); + void unregister_clause(clause* cls); + void pure_decompose(); + void pure_decompose(literal lit); + void pure_decompose(ptr_vector& uses, svector& clauses); + void post_decompose(); + literal find_blocked(clause const& cls); + bool bce(clause& cls); + bool is_blocked(literal lit) const; + void init_rbits(); + void init_reconstruction_stack(); + void sat_sweep(); + void cleanup(); + uint64 eval_clause(clause const& cls) const; + void verify_sweep(); + void extract_partition(); + bool check_equality(unsigned v1, unsigned v2); + bool is_already_equiv(literal l1, literal l2); + void assert_equality(literal l1, literal l2); + public: + bceq(solver & s); + void operator()(); + }; + +}; + +#endif diff --git a/src/sat/sat_clause.h b/src/sat/sat_clause.h index 87fc5e763..6a183c3df 100644 --- a/src/sat/sat_clause.h +++ b/src/sat/sat_clause.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_CLAUSE_H_ -#define _SAT_CLAUSE_H_ +#ifndef SAT_CLAUSE_H_ +#define SAT_CLAUSE_H_ #include"sat_types.h" #include"small_object_allocator.h" diff --git a/src/sat/sat_clause_set.h b/src/sat/sat_clause_set.h index ac95e06d5..e63c92d08 100644 --- a/src/sat/sat_clause_set.h +++ b/src/sat/sat_clause_set.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_CLAUSE_SET_H_ -#define _SAT_CLAUSE_SET_H_ +#ifndef SAT_CLAUSE_SET_H_ +#define SAT_CLAUSE_SET_H_ #include"sat_clause.h" diff --git a/src/sat/sat_clause_use_list.h b/src/sat/sat_clause_use_list.h index 0859439d5..ec283f4c4 100644 --- a/src/sat/sat_clause_use_list.h +++ b/src/sat/sat_clause_use_list.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_CLAUSE_USE_LIST_H_ -#define _SAT_CLAUSE_USE_LIST_H_ +#ifndef SAT_CLAUSE_USE_LIST_H_ +#define SAT_CLAUSE_USE_LIST_H_ #include"sat_types.h" #include"trace.h" diff --git a/src/sat/sat_cleaner.h b/src/sat/sat_cleaner.h index d22408926..d886453df 100644 --- a/src/sat/sat_cleaner.h +++ b/src/sat/sat_cleaner.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_CLEANER_H_ -#define _SAT_CLEANER_H_ +#ifndef SAT_CLEANER_H_ +#define SAT_CLEANER_H_ #include"sat_types.h" #include"statistics.h" diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index fb81809b1..aff023b22 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -107,6 +107,10 @@ namespace sat { m_gc_increment = p.gc_increment(); } m_minimize_lemmas = p.minimize_lemmas(); + m_minimize_core = p.minimize_core(); + m_minimize_core_partial = p.minimize_core_partial(); + m_optimize_model = p.optimize_model(); + m_bcd = p.bcd(); m_dyn_sub_res = p.dyn_sub_res(); } diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index 253786e11..2eff402ad 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _SAT_CONFIG_H_ -#define _SAT_CONFIG_H_ +#ifndef SAT_CONFIG_H_ +#define SAT_CONFIG_H_ #include"params.h" @@ -69,6 +69,11 @@ namespace sat { bool m_minimize_lemmas; bool m_dyn_sub_res; + bool m_minimize_core; + bool m_minimize_core_partial; + bool m_optimize_model; + bool m_bcd; + symbol m_always_true; symbol m_always_false; diff --git a/src/sat/sat_elim_eqs.h b/src/sat/sat_elim_eqs.h index 21a653383..6f81d6ecb 100644 --- a/src/sat/sat_elim_eqs.h +++ b/src/sat/sat_elim_eqs.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_ELIM_EQS_H_ -#define _SAT_ELIM_EQS_H_ +#ifndef SAT_ELIM_EQS_H_ +#define SAT_ELIM_EQS_H_ #include"sat_types.h" diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index a2b43675b..b0b1c5d96 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_EXTENSION_H_ -#define _SAT_EXTENSION_H_ +#ifndef SAT_EXTENSION_H_ +#define SAT_EXTENSION_H_ #include"sat_types.h" #include"params.h" diff --git a/src/sat/sat_iff3_finder.h b/src/sat/sat_iff3_finder.h index 6522375bb..a74ad1eb1 100644 --- a/src/sat/sat_iff3_finder.h +++ b/src/sat/sat_iff3_finder.h @@ -26,8 +26,8 @@ Author: Revision History: --*/ -#ifndef _SAT_IFF3_FINDER_H_ -#define _SAT_IFF3_FINDER_H_ +#ifndef SAT_IFF3_FINDER_H_ +#define SAT_IFF3_FINDER_H_ #include"sat_types.h" diff --git a/src/sat/sat_integrity_checker.h b/src/sat/sat_integrity_checker.h index 8aa9340c0..5eef1748e 100644 --- a/src/sat/sat_integrity_checker.h +++ b/src/sat/sat_integrity_checker.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _SAT_INTEGRITY_CHECKER_H_ -#define _SAT_INTEGRITY_CHECKER_H_ +#ifndef SAT_INTEGRITY_CHECKER_H_ +#define SAT_INTEGRITY_CHECKER_H_ #include"sat_types.h" diff --git a/src/sat/sat_justification.h b/src/sat/sat_justification.h index dd803feeb..00da9f30e 100644 --- a/src/sat/sat_justification.h +++ b/src/sat/sat_justification.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_JUSTIFICATIONS_H_ -#define _SAT_JUSTIFICATIONS_H_ +#ifndef SAT_JUSTIFICATIONS_H_ +#define SAT_JUSTIFICATIONS_H_ namespace sat { @@ -30,9 +30,9 @@ namespace sat { justification(ext_justification_idx idx, kind k):m_val1(idx), m_val2(k) {} public: justification():m_val1(0), m_val2(NONE) {} - justification(literal l):m_val1(l.to_uint()), m_val2(BINARY) {} + explicit justification(literal l):m_val1(l.to_uint()), m_val2(BINARY) {} justification(literal l1, literal l2):m_val1(l1.to_uint()), m_val2(TERNARY + (l2.to_uint() << 3)) {} - justification(clause_offset cls_off):m_val1(cls_off), m_val2(CLAUSE) {} + explicit justification(clause_offset cls_off):m_val1(cls_off), m_val2(CLAUSE) {} justification mk_ext_justification(ext_justification_idx idx) { return justification(idx, EXT_JUSTIFICATION); } kind get_kind() const { return static_cast(m_val2 & 7); } @@ -52,6 +52,27 @@ namespace sat { bool is_ext_justification() const { return m_val2 == EXT_JUSTIFICATION; } ext_justification_idx get_ext_justification_idx() const { return m_val1; } }; + + inline std::ostream & operator<<(std::ostream & out, justification const & j) { + switch (j.get_kind()) { + case justification::NONE: + out << "none"; + break; + case justification::BINARY: + out << "binary " << j.get_literal(); + break; + case justification::TERNARY: + out << "ternary " << j.get_literal1() << " " << j.get_literal2(); + break; + case justification::CLAUSE: + out << "clause"; + break; + case justification::EXT_JUSTIFICATION: + out << "external"; + break; + } + return out; + } }; #endif diff --git a/src/sat/sat_model_converter.h b/src/sat/sat_model_converter.h index f2c35e364..b89e6e784 100644 --- a/src/sat/sat_model_converter.h +++ b/src/sat/sat_model_converter.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_MODEL_CONVERTER_H_ -#define _SAT_MODEL_CONVERTER_H_ +#ifndef SAT_MODEL_CONVERTER_H_ +#define SAT_MODEL_CONVERTER_H_ #include"sat_types.h" diff --git a/src/sat/sat_mus.cpp b/src/sat/sat_mus.cpp new file mode 100644 index 000000000..9791db118 --- /dev/null +++ b/src/sat/sat_mus.cpp @@ -0,0 +1,288 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sat_mus.cpp + +Abstract: + + Faster MUS extraction + +Author: + + Nikolaj Bjorner (nbjorner) 2014-20-7 + +Notes: + + +--*/ + +#include "sat_solver.h" +#include "sat_mus.h" +#include "sat_sls.h" + +namespace sat { + + mus::mus(solver& s):s(s), m_is_active(false), m_best_value(0), m_restart(0), m_max_restarts(0) {} + + mus::~mus() {} + + void mus::reset() { + m_core.reset(); + m_mus.reset(); + m_model.reset(); + m_best_value = 0; + m_max_restarts = (s.m_stats.m_restart - m_restart) + 10; + m_restart = s.m_stats.m_restart; + } + + void mus::set_core() { + m_mus.append(m_core); + s.m_core.reset(); + s.m_core.append(m_mus); + TRACE("sat", tout << "new core: " << s.m_core << "\n";); + } + + void mus::update_model() { + double new_value = s.m_wsls.evaluate_model(s.m_model); + if (m_model.empty()) { + m_model.append(s.m_model); + m_best_value = new_value; + } + else if (m_best_value > new_value) { + m_model.reset(); + m_model.append(s.m_model); + m_best_value = new_value; + } + } + + lbool mus::operator()() { + flet _disable_min(s.m_config.m_minimize_core, false); + flet _disable_opt(s.m_config.m_optimize_model, false); + flet _is_active(m_is_active, true); + IF_VERBOSE(3, verbose_stream() << "(sat.mus " << s.get_core() << ")\n";); + reset(); + lbool r = mus1(); + m_restart = s.m_stats.m_restart; + return r; + } + + lbool mus::mus1() { + bool minimize_partial = s.m_config.m_minimize_core_partial; + TRACE("sat", tout << "old core: " << s.get_core() << "\n";); + literal_vector& core = get_core(); + literal_vector& mus = m_mus; + if (core.size() > 64) { + return mus2(); + } + unsigned delta_time = 0; + unsigned core_miss = 0; + while (!core.empty()) { + IF_VERBOSE(3, verbose_stream() << "(opt.mus reducing core: " << core.size() << " mus: " << mus.size() << ")\n";); + TRACE("sat", + tout << "core: " << core << "\n"; + tout << "mus: " << mus << "\n";); + + if (s.m_cancel) { + set_core(); + return l_undef; + } + if (minimize_partial && 3*delta_time > core.size() && core.size() < mus.size()) { + break; + } + unsigned num_literals = core.size() + mus.size(); + if (num_literals <= 2) { + // IF_VERBOSE(0, verbose_stream() << "num literals: " << core << " " << mus << "\n";); + break; + } + if (s.m_config.m_minimize_core_partial && s.m_stats.m_restart - m_restart > m_max_restarts) { + IF_VERBOSE(1, verbose_stream() << "restart budget exceeded\n";); + set_core(); + return l_true; + } + + literal lit = core.back(); + core.pop_back(); + lbool is_sat; + { + scoped_append _sa(mus, core); + mus.push_back(~lit); + is_sat = s.check(mus.size(), mus.c_ptr()); + TRACE("sat", tout << "mus: " << mus << "\n";); + } + switch (is_sat) { + case l_undef: + core.push_back(lit); + set_core(); + return l_undef; + case l_true: { + SASSERT(value_at(lit, s.get_model()) == l_false); + mus.push_back(lit); + update_model(); + if (!core.empty()) { + // mr(); // TBD: measure + } + break; + } + case l_false: + literal_vector const& new_core = s.get_core(); + if (new_core.contains(~lit)) { + IF_VERBOSE(3, verbose_stream() << "miss core " << lit << "\n";); + ++core_miss; + } + else { + core_miss = 0; + TRACE("sat", tout << "core: " << new_core << " mus: " << mus << "\n";); + core.reset(); + for (unsigned i = 0; i < new_core.size(); ++i) { + literal lit = new_core[i]; + if (!mus.contains(lit)) { + core.push_back(lit); + } + } + } + break; + } + + unsigned new_num_literals = core.size() + mus.size(); + if (new_num_literals == num_literals) { + delta_time++; + } + else { + delta_time = 0; + } + } + set_core(); + IF_VERBOSE(3, verbose_stream() << "(sat.mus.new " << s.m_core << ")\n";); + return l_true; + } + + + // bisection search. + lbool mus::mus2() { + literal_set core(get_core()); + literal_set support; + lbool is_sat = qx(core, support, false); + s.m_core.reset(); + s.m_core.append(core.to_vector()); + IF_VERBOSE(3, verbose_stream() << "(sat.mus.new " << s.m_core << ")\n";); + return is_sat; + } + + lbool mus::qx(literal_set& assignment, literal_set& support, bool has_support) { + lbool is_sat = l_true; + if (s.m_config.m_minimize_core_partial && s.m_stats.m_restart - m_restart > m_max_restarts) { + IF_VERBOSE(1, verbose_stream() << "restart budget exceeded\n";); + return l_true; + } + if (has_support) { + scoped_append _sa(m_mus, support.to_vector()); + is_sat = s.check(m_mus.size(), m_mus.c_ptr()); + switch (is_sat) { + case l_false: { + literal_set core(s.get_core()); + support &= core; + assignment.reset(); + return l_true; + } + case l_undef: + return l_undef; + case l_true: + update_model(); + break; + default: + break; + } + } + if (assignment.size() == 1) { + return l_true; + } + literal_set assign2; + split(assignment, assign2); + support |= assignment; + is_sat = qx(assign2, support, !assignment.empty()); + unsplit(support, assignment); + if (is_sat != l_true) return is_sat; + support |= assign2; + is_sat = qx(assignment, support, !assign2.empty()); + assignment |= assign2; + unsplit(support, assign2); + return is_sat; + } + + void mus::unsplit(literal_set& A, literal_set& B) { + literal_set A1, B1; + literal_set::iterator it = A.begin(), end = A.end(); + for (; it != end; ++it) { + if (B.contains(*it)) { + B1.insert(*it); + } + else { + A1.insert(*it); + } + } + A = A1; + B = B1; + } + + void mus::split(literal_set& lits1, literal_set& lits2) { + unsigned half = lits1.size()/2; + literal_set lits3; + literal_set::iterator it = lits1.begin(), end = lits1.end(); + for (unsigned i = 0; it != end; ++it, ++i) { + if (i < half) { + lits3.insert(*it); + } + else { + lits2.insert(*it); + } + } + lits1 = lits3; + } + + literal_vector& mus::get_core() { + m_core.reset(); + m_mus.reset(); + literal_vector& core = m_core; + core.append(s.get_core()); + for (unsigned i = 0; i < core.size(); ++i) { + if (s.m_user_scope_literals.contains(core[i])) { + m_mus.push_back(core[i]); + core[i] = core.back(); + core.pop_back(); + --i; + } + } + return core; + } + + void mus::verify_core(literal_vector const& core) { + lbool is_sat = s.check(core.size(), core.c_ptr()); + IF_VERBOSE(3, verbose_stream() << "core verification: " << is_sat << " " << core << "\n";); + } + + void mus::mr() { + sls sls(s); + literal_vector tabu; + tabu.append(m_mus); + tabu.append(m_core); + bool reuse_model = false; + for (unsigned i = m_mus.size(); i < tabu.size(); ++i) { + tabu[i] = ~tabu[i]; + lbool is_sat = sls(tabu.size(), tabu.c_ptr(), reuse_model); + tabu[i] = ~tabu[i]; + if (is_sat == l_true) { + m_mus.push_back(tabu[i]); + m_core.erase(tabu[i]); + IF_VERBOSE(3, verbose_stream() << "in core " << tabu[i] << "\n";); + reuse_model = true; + } + else { + IF_VERBOSE(3, verbose_stream() << "NOT in core " << tabu[i] << "\n";); + reuse_model = false; + } + } + } +} + diff --git a/src/sat/sat_mus.h b/src/sat/sat_mus.h new file mode 100644 index 000000000..617bbc757 --- /dev/null +++ b/src/sat/sat_mus.h @@ -0,0 +1,71 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + mus.h + +Abstract: + + Faster MUS extraction based on Belov et.al. HYB (Algorithm 3, 4) + +Author: + + Nikolaj Bjorner (nbjorner) 2014-20-7 + +Notes: + +--*/ +#ifndef SAT_MUS_H_ +#define SAT_MUS_H_ + +namespace sat { + class mus { + solver& s; + literal_vector m_core; + literal_vector m_mus; + bool m_is_active; + model m_model; // model obtained during minimal unsat core + double m_best_value; + unsigned m_restart; + unsigned m_max_restarts; + + + public: + mus(solver& s); + ~mus(); + lbool operator()(); + bool is_active() const { return m_is_active; } + model const& get_model() const { return m_model; } + private: + lbool mus1(); + lbool mus2(); + lbool qx(literal_set& assignment, literal_set& support, bool has_support); + void mr(); + void reset(); + void set_core(); + void update_model(); + literal_vector & get_core(); + void verify_core(literal_vector const& lits); + void split(literal_set& src, literal_set& dst); + void intersect(literal_set& dst, literal_set const& src); + void unsplit(literal_set& A, literal_set& B); + class scoped_append { + unsigned m_size; + literal_vector& m_lits; + public: + scoped_append(literal_vector& lits, literal_vector const& other): + m_size(lits.size()), + m_lits(lits) { + m_lits.append(other); + } + ~scoped_append() { + m_lits.resize(m_size); + } + + }; + }; + +}; + +#endif diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index de0283897..c5ac97b4e 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -18,4 +18,9 @@ def_module_params('sat', ('gc.small_lbd', UINT, 3, 'learned clauses with small LBD are never deleted (only used in dyn_psm)'), ('gc.k', UINT, 7, 'learned clauses that are inactive for k gc rounds are permanently deleted (only used in dyn_psm)'), ('minimize_lemmas', BOOL, True, 'minimize learned clauses'), - ('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'))) + ('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'), + ('minimize_core', BOOL, False, 'minimize computed core'), + ('minimize_core_partial', BOOL, False, 'apply partial (cheap) core minimization'), + ('optimize_model', BOOL, False, 'enable optimization of soft constraints'), + ('bcd', BOOL, False, 'enable blocked clause decomposition for equality extraction'), + ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'))) diff --git a/src/sat/sat_probing.h b/src/sat/sat_probing.h index 2061b74bd..daf6817e3 100644 --- a/src/sat/sat_probing.h +++ b/src/sat/sat_probing.h @@ -17,8 +17,8 @@ Author: Revision History: --*/ -#ifndef _SAT_PROBING_H_ -#define _SAT_PROBING_H_ +#ifndef SAT_PROBING_H_ +#define SAT_PROBING_H_ #include"sat_types.h" #include"params.h" diff --git a/src/sat/sat_scc.h b/src/sat/sat_scc.h index 5f69e11c6..f44c39742 100644 --- a/src/sat/sat_scc.h +++ b/src/sat/sat_scc.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_SCC_H_ -#define _SAT_SCC_H_ +#ifndef SAT_SCC_H_ +#define SAT_SCC_H_ #include"sat_types.h" #include"statistics.h" diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index e67043c04..db62febbd 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -137,8 +137,10 @@ namespace sat { return; if (!m_subsumption && !m_elim_blocked_clauses && !m_resolution) return; + CASSERT("sat_solver", s.check_invariant()); TRACE("before_simplifier", s.display(tout);); + s.m_cleaner(true); m_last_sub_trail_sz = s.m_trail.size(); TRACE("after_cleanup", s.display(tout);); @@ -156,6 +158,7 @@ namespace sat { if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) elim_blocked_clauses(); + if (!learned) m_num_calls++; @@ -179,23 +182,21 @@ namespace sat { bool vars_eliminated = m_num_elim_vars > old_num_elim_vars; - if (!m_need_cleanup) { + if (m_need_cleanup) { + TRACE("after_simplifier", tout << "cleanning watches...\n";); + cleanup_watches(); + cleanup_clauses(s.m_learned, true, vars_eliminated, learned_in_use_lists); + cleanup_clauses(s.m_clauses, false, vars_eliminated, true); + } + else { TRACE("after_simplifier", tout << "skipping cleanup...\n";); if (vars_eliminated) { // must remove learned clauses with eliminated variables cleanup_clauses(s.m_learned, true, true, learned_in_use_lists); } - CASSERT("sat_solver", s.check_invariant()); - TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); - free_memory(); - return; } - TRACE("after_simplifier", tout << "cleanning watches...\n";); - cleanup_watches(); - cleanup_clauses(s.m_learned, true, vars_eliminated, learned_in_use_lists); - cleanup_clauses(s.m_clauses, false, vars_eliminated, true); - TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); CASSERT("sat_solver", s.check_invariant()); + TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); free_memory(); } @@ -921,7 +922,6 @@ namespace sat { model_converter::entry * new_entry = 0; if (s.is_external(l.var()) || s.was_eliminated(l.var())) return; - { m_to_remove.reset(); @@ -1179,7 +1179,6 @@ namespace sat { continue; m_visited[l2.index()] = false; } - return res; } @@ -1427,6 +1426,7 @@ namespace sat { }; void simplifier::elim_vars() { + if (!m_elim_vars) return; elim_var_report rpt(*this); bool_var_vector vars; order_vars_for_elim(vars); @@ -1466,6 +1466,7 @@ namespace sat { m_res_cls_cutoff2 = p.resolution_cls_cutoff2(); m_subsumption = p.subsumption(); m_subsumption_limit = p.subsumption_limit(); + m_elim_vars = p.elim_vars(); } void simplifier::collect_param_descrs(param_descrs & r) { diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index cb6fa9557..85922cf7c 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -18,8 +18,8 @@ Author: Revision History: --*/ -#ifndef _SAT_SIMPLIFIER_H_ -#define _SAT_SIMPLIFIER_H_ +#ifndef SAT_SIMPLIFIER_H_ +#define SAT_SIMPLIFIER_H_ #include"sat_types.h" #include"sat_clause.h" @@ -82,6 +82,7 @@ namespace sat { bool m_subsumption; unsigned m_subsumption_limit; + bool m_elim_vars; // stats unsigned m_num_blocked_clauses; @@ -172,6 +173,7 @@ namespace sat { ~simplifier(); void insert_todo(bool_var v) { m_elim_todo.insert(v); } + void reset_todo() { m_elim_todo.reset(); } void operator()(bool learned); diff --git a/src/sat/sat_simplifier_params.pyg b/src/sat/sat_simplifier_params.pyg index 6165e7e62..ff2944987 100644 --- a/src/sat/sat_simplifier_params.pyg +++ b/src/sat/sat_simplifier_params.pyg @@ -15,5 +15,6 @@ def_module_params(module_name='sat', ('resolution.lit_cutoff_range3', UINT, 300, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff2'), ('resolution.cls_cutoff1', UINT, 100000000, 'limit1 - total number of problems clauses for the second cutoff of Boolean variable elimination'), ('resolution.cls_cutoff2', UINT, 700000000, 'limit2 - total number of problems clauses for the second cutoff of Boolean variable elimination'), + ('elim_vars', BOOL, True, 'enable variable elimination during simplification'), ('subsumption', BOOL, True, 'eliminate subsumed clauses'), ('subsumption.limit', UINT, 100000000, 'approx. maximum number of literals visited during subsumption (and subsumption resolution)'))) diff --git a/src/sat/sat_sls.cpp b/src/sat/sat_sls.cpp new file mode 100644 index 000000000..24f952ea9 --- /dev/null +++ b/src/sat/sat_sls.cpp @@ -0,0 +1,685 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sat_sls.cpp + +Abstract: + + SLS for clauses in SAT solver + +Author: + + Nikolaj Bjorner (nbjorner) 2014-12-8 + +Notes: + +--*/ + +#include "sat_sls.h" +#include "sat_solver.h" + +namespace sat { + + bool index_set::contains(unsigned idx) const { + return + (idx < m_index.size()) && + (m_index[idx] < m_elems.size()) && + (m_elems[m_index[idx]] == idx); + } + + void index_set::insert(unsigned idx) { + m_index.reserve(idx+1); + if (!contains(idx)) { + m_index[idx] = m_elems.size(); + m_elems.push_back(idx); + } + } + + void index_set::remove(unsigned idx) { + if (!contains(idx)) return; + unsigned pos = m_index[idx]; + m_elems[pos] = m_elems.back(); + m_index[m_elems[pos]] = pos; + m_elems.pop_back(); + } + + unsigned index_set::choose(random_gen& rnd) const { + SASSERT(!empty()); + return m_elems[rnd(num_elems())]; + } + + sls::sls(solver& s): s(s), m_cancel(false) { + m_prob_choose_min_var = 43; + m_clause_generation = 0; + } + + sls::~sls() { + for (unsigned i = 0; i < m_bin_clauses.size(); ++i) { + m_alloc.del_clause(m_bin_clauses[i]); + } + } + + lbool sls::operator()(unsigned sz, literal const* tabu, bool reuse_model) { + init(sz, tabu, reuse_model); + unsigned i; + for (i = 0; !m_false.empty() && !m_cancel && i < m_max_tries; ++i) { + flip(); + } + IF_VERBOSE(2, verbose_stream() << "tries " << i << "\n";); + if (m_false.empty()) { + SASSERT(s.check_model(m_model)); + return l_true; + } + return l_undef; + } + + void sls::init(unsigned sz, literal const* tabu, bool reuse_model) { + bool same_generation = (m_clause_generation == s.m_stats.m_non_learned_generation); + if (!same_generation) { + init_clauses(); + init_use(); + IF_VERBOSE(0, verbose_stream() << s.m_stats.m_non_learned_generation << " " << m_clause_generation << "\n";); + } + if (!reuse_model) { + init_model(); + } + init_tabu(sz, tabu); + m_clause_generation = s.m_stats.m_non_learned_generation; + + m_max_tries = 10*(s.num_vars() + m_clauses.size()); + + } + + void sls::init_clauses() { + for (unsigned i = 0; i < m_bin_clauses.size(); ++i) { + m_alloc.del_clause(m_bin_clauses[i]); + } + m_bin_clauses.reset(); + m_clauses.reset(); + clause * const * it = s.begin_clauses(); + clause * const * end = s.end_clauses(); + for (; it != end; ++it) { + m_clauses.push_back(*it); + } + svector bincs; + s.collect_bin_clauses(bincs, false); + literal lits[2]; + for (unsigned i = 0; i < bincs.size(); ++i) { + lits[0] = bincs[i].first; + lits[1] = bincs[i].second; + clause* cl = m_alloc.mk_clause(2, lits, false); + m_clauses.push_back(cl); + m_bin_clauses.push_back(cl); + } + } + + void sls::init_model() { + m_num_true.reset(); + m_model.reset(); + m_model.append(s.get_model()); + unsigned sz = m_clauses.size(); + for (unsigned i = 0; i < sz; ++i) { + clause const& c = *m_clauses[i]; + unsigned n = 0; + unsigned csz = c.size(); + for (unsigned j = 0; j < csz; ++j) { + lbool val = value_at(c[j], m_model); + switch (val) { + case l_true: + ++n; + break; + case l_undef: + ++n; + m_model[c[j].var()] = c[j].sign()?l_false:l_true; + SASSERT(value_at(c[j], m_model) == l_true); + break; + default: + break; + } + } + m_num_true.push_back(n); + if (n == 0) { + m_false.insert(i); + } + } + } + + void sls::init_tabu(unsigned sz, literal const* tabu) { + // our main use is where m_model satisfies all the hard constraints. + // SASSERT(s.check_model(m_model)); + // SASSERT(m_false.empty()); + // ASSERT: m_num_true is correct count. + m_tabu.reset(); + m_tabu.resize(s.num_vars(), false); + for (unsigned i = 0; i < sz; ++i) { + literal lit = tabu[i]; + if (s.m_level[lit.var()] == 0) continue; + if (value_at(lit, m_model) == l_false) { + flip(lit); + } + m_tabu[lit.var()] = true; + } + for (unsigned i = 0; i < s.m_trail.size(); ++i) { + literal lit = s.m_trail[i]; + if (s.m_level[lit.var()] > 0) break; + if (value_at(lit, m_model) != l_true) { + flip(lit); + } + m_tabu[lit.var()] = true; + } + } + + void sls::init_use() { + m_use_list.reset(); + m_use_list.resize(s.num_vars()*2); + unsigned sz = m_clauses.size(); + for (unsigned i = 0; i < sz; ++i) { + clause const& c = *m_clauses[i]; + unsigned csz = c.size(); + for (unsigned j = 0; j < csz; ++j) { + m_use_list[c[j].index()].push_back(i); + } + } + DEBUG_CODE(check_use_list();); + } + + unsigned_vector const& sls::get_use(literal lit) { + SASSERT(lit.index() < m_use_list.size()); + return m_use_list[lit.index()]; + } + + unsigned sls::get_break_count(literal lit, unsigned min_break) { + SASSERT(value_at(lit, m_model) == l_false); + unsigned result = 0; + unsigned_vector const& uses = get_use(~lit); + unsigned sz = uses.size(); + for (unsigned i = 0; i < sz; ++i) { + if (m_num_true[uses[i]] == 1) { + ++result; + if (result > min_break) return result; + } + } + return result; + } + + bool sls::pick_flip(literal& lit) { + unsigned clause_idx = m_false.choose(m_rand); + clause const& c = *m_clauses[clause_idx]; + SASSERT(!c.satisfied_by(m_model)); + unsigned min_break = UINT_MAX; + unsigned sz = c.size(); + m_min_vars.reset(); + for (unsigned i = 0; i < sz; ++i) { + lit = c[i]; + if (m_tabu[lit.var()]) continue; + unsigned break_count = get_break_count(lit, min_break); + if (break_count < min_break) { + min_break = break_count; + m_min_vars.reset(); + m_min_vars.push_back(lit); + } + else if (break_count == min_break) { + m_min_vars.push_back(lit); + } + } + if (min_break == 0 || (!m_min_vars.empty() && m_rand(100) >= m_prob_choose_min_var)) { + lit = m_min_vars[m_rand(m_min_vars.size())]; + return true; + } + else if (min_break == UINT_MAX) { + return false; + } + else { + lit = c[m_rand(c.size())]; + return !m_tabu[lit.var()]; + } + } + + void sls::flip() { + literal lit; + if (pick_flip(lit)) { + flip(lit); + } + } + + void sls::flip(literal lit) { + //IF_VERBOSE(0, verbose_stream() << lit << " ";); + SASSERT(value_at(lit, m_model) == l_false); + SASSERT(!m_tabu[lit.var()]); + m_model[lit.var()] = lit.sign()?l_false:l_true; + SASSERT(value_at(lit, m_model) == l_true); + unsigned_vector const& use1 = get_use(lit); + unsigned sz = use1.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned cl = use1[i]; + m_num_true[cl]++; + SASSERT(m_num_true[cl] <= m_clauses[cl]->size()); + if (m_num_true[cl] == 1) m_false.remove(cl); + } + unsigned_vector const& use2 = get_use(~lit); + sz = use2.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned cl = use2[i]; + SASSERT(m_num_true[cl] > 0); + m_num_true[cl]--; + if (m_num_true[cl] == 0) m_false.insert(cl); + } + } + + void sls::check_invariant() { + for (unsigned i = 0; i < m_clauses.size(); ++i) { + clause const& c = *m_clauses[i]; + bool is_sat = c.satisfied_by(m_model); + SASSERT(is_sat != m_false.contains(i)); + SASSERT(is_sat == (m_num_true[i] > 0)); + } + } + + void sls::check_use_list() { + + for (unsigned i = 0; i < m_clauses.size(); ++i) { + clause const& c = *m_clauses[i]; + for (unsigned j = 0; j < c.size(); ++j) { + unsigned idx = c[j].index(); + SASSERT(m_use_list[idx].contains(i)); + } + } + + for (unsigned i = 0; i < m_use_list.size(); ++i) { + literal lit = to_literal(i); + for (unsigned j = 0; j < m_use_list[i].size(); ++j) { + clause const& c = *m_clauses[m_use_list[i][j]]; + bool found = false; + for (unsigned k = 0; !found && k < c.size(); ++k) { + found = c[k] == lit; + } + SASSERT(found); + } + } + } + + void sls::display(std::ostream& out) const { + out << "Model\n"; + for (bool_var v = 0; v < m_model.size(); ++v) { + out << v << ": " << m_model[v] << "\n"; + } + out << "Clauses\n"; + unsigned sz = m_false.num_elems(); + for (unsigned i = 0; i < sz; ++i) { + out << *m_clauses[m_false[i]] << "\n"; + } + for (unsigned i = 0; i < m_clauses.size(); ++i) { + if (m_false.contains(i)) continue; + clause const& c = *m_clauses[i]; + out << c << " " << m_num_true[i] << "\n"; + } + bool has_tabu = false; + for (unsigned i = 0; !has_tabu && i < m_tabu.size(); ++i) { + has_tabu = m_tabu[i]; + } + if (has_tabu) { + out << "Tabu: "; + for (unsigned i = 0; i < m_tabu.size(); ++i) { + if (m_tabu[i]) { + literal lit(i, false); + if (value_at(lit, m_model) == l_false) lit.neg(); + out << lit << " "; + } + } + out << "\n"; + } + } + + + wsls::wsls(solver& s): + sls(s) + { + m_smoothing_probability = 1; // 1/1000 + } + + wsls::~wsls() {} + + void wsls::set_soft(unsigned sz, literal const* lits, double const* weights) { + m_soft.reset(); + m_weights.reset(); + m_soft.append(sz, lits); + m_weights.append(sz, weights); + } + + void wsls::opt(unsigned sz, literal const* tabu, bool reuse_model) { + init(sz, tabu, reuse_model); + + // + // Initialize m_clause_weights, m_hscore, m_sscore. + // + m_best_value = m_false.empty()?evaluate_model(m_model):-1.0; + m_best_model.reset(); + m_clause_weights.reset(); + m_hscore.reset(); + m_sscore.reset(); + m_H.reset(); + m_S.reset(); + m_best_model.append(s.get_model()); + m_clause_weights.resize(m_clauses.size(), 1); + m_sscore.resize(s.num_vars(), 0.0); + m_hscore.resize(s.num_vars(), 0); + for (unsigned i = 0; i < m_soft.size(); ++i) { + literal lit = m_soft[i]; + m_sscore[lit.var()] = m_weights[i]; + if (value_at(lit, m_model) == l_true) { + m_sscore[lit.var()] = -m_sscore[lit.var()]; + } + } + for (bool_var i = 0; i < s.num_vars(); ++i) { + m_hscore[i] = compute_hscore(i); + refresh_scores(i); + } + DEBUG_CODE(check_invariant();); + unsigned i = 0; + for (; !m_cancel && m_best_value > 0 && i < m_max_tries; ++i) { + wflip(); + if (m_false.empty()) { + double val = evaluate_model(m_model); + if (val < m_best_value || m_best_value < 0.0) { + m_best_value = val; + m_best_model.reset(); + m_best_model.append(m_model); + s.set_model(m_best_model); + IF_VERBOSE(1, verbose_stream() << "new value: " << val << " @ " << i << "\n";); + if (i*2 > m_max_tries) { + m_max_tries *= 2; + } + } + } + } + TRACE("sat", display(tout);); + IF_VERBOSE(0, verbose_stream() << "tries " << i << "\n";); + } + + void wsls::wflip() { + literal lit; + if (pick_wflip(lit)) { + // IF_VERBOSE(0, verbose_stream() << lit << " ";); + wflip(lit); + } + } + + bool wsls::pick_wflip(literal & lit) { + unsigned idx; + if (!m_H.empty()) { + idx = m_H.choose(m_rand); + lit = literal(idx, false); + if (value_at(lit, m_model) == l_true) lit.neg(); + SASSERT(value_at(lit, m_model) == l_false); + TRACE("sat", tout << "flip H(" << m_H.num_elems() << ") " << lit << "\n";); + } + else if (!m_S.empty()) { + double score = 0.0; + m_min_vars.reset(); + for (unsigned i = 0; i < m_S.num_elems(); ++i) { + unsigned v = m_S[i]; + SASSERT(m_sscore[v] > 0.0); + if (m_sscore[v] > score) { + m_min_vars.reset(); + m_min_vars.push_back(literal(v, false)); + score = m_sscore[v]; + } + else if (m_sscore[v] == score) { + m_min_vars.push_back(literal(v, false)); + } + } + lit = m_min_vars[m_rand(m_min_vars.size())]; // pick with largest sscore. + SASSERT(value_at(lit, m_model) == l_false); + TRACE("sat", tout << "flip S(" << m_min_vars.size() << "," << score << ") " << lit << "\n";); + } + else { + update_hard_weights(); + if (!m_false.empty()) { + unsigned cls_idx = m_false.choose(m_rand); + clause const& c = *m_clauses[cls_idx]; + lit = c[m_rand(c.size())]; + TRACE("sat", tout << "flip hard(" << m_false.num_elems() << "," << c.size() << ") " << lit << "\n";); + } + else { + m_min_vars.reset(); + for (unsigned i = 0; i < m_soft.size(); ++i) { + lit = m_soft[i]; + if (value_at(lit, m_model) == l_false) { + m_min_vars.push_back(lit); + } + } + if (m_min_vars.empty()) { + SASSERT(m_best_value == 0.0); + UNREACHABLE(); // we should have exited the main loop before. + return false; + } + else { + lit = m_min_vars[m_rand(m_min_vars.size())]; + } + TRACE("sat", tout << "flip soft(" << m_min_vars.size() << ", " << m_sscore[lit.var()] << ") " << lit << "\n";); + + } + SASSERT(value_at(lit, m_model) == l_false); + } + return !m_tabu[lit.var()]; + } + + void wsls::wflip(literal lit) { + flip(lit); + unsigned v = lit.var(); + m_sscore[v] = -m_sscore[v]; + m_hscore[v] = compute_hscore(v); + refresh_scores(v); + recompute_hscores(lit); + } + + void wsls::update_hard_weights() { + unsigned csz = m_clauses.size(); + if (m_smoothing_probability >= m_rand(1000)) { + for (unsigned i = 0; i < csz; ++i) { + if (m_clause_weights[i] > 1 && !m_false.contains(i)) { + --m_clause_weights[i]; + if (m_num_true[i] == 1) { + clause const& c = *m_clauses[i]; + unsigned sz = c.size(); + for (unsigned j = 0; j < sz; ++j) { + if (value_at(c[j], m_model) == l_true) { + ++m_hscore[c[j].var()]; + refresh_scores(c[j].var()); + break; + } + } + } + } + } + } + else { + for (unsigned i = 0; i < csz; ++i) { + if (m_false.contains(i)) { + ++m_clause_weights[i]; + clause const& c = *m_clauses[i]; + unsigned sz = c.size(); + for (unsigned j = 0; j < sz; ++j) { + ++m_hscore[c[j].var()]; + refresh_scores(c[j].var()); + } + } + } + } + DEBUG_CODE(check_invariant();); + } + + double wsls::evaluate_model(model& mdl) { + SASSERT(m_false.empty()); + double result = 0.0; + for (unsigned i = 0; i < m_soft.size(); ++i) { + literal lit = m_soft[i]; + if (value_at(lit, mdl) != l_true) { + result += m_weights[i]; + } + } + return result; + } + + int wsls::compute_hscore(bool_var v) { + literal lit(v, false); + if (value_at(lit, m_model) == l_false) { + lit.neg(); + } + SASSERT(value_at(lit, m_model) == l_true); + int hs = 0; + unsigned_vector const& use1 = get_use(~lit); + unsigned sz = use1.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned cl = use1[i]; + if (m_num_true[cl] == 0) { + SASSERT(m_false.contains(cl)); + hs += m_clause_weights[cl]; + } + else { + SASSERT(!m_false.contains(cl)); + } + } + unsigned_vector const& use2 = get_use(lit); + sz = use2.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned cl = use2[i]; + if (m_num_true[cl] == 1) { + SASSERT(!m_false.contains(cl)); + hs -= m_clause_weights[cl]; + } + } + return hs; + } + + void wsls::recompute_hscores(literal lit) { + SASSERT(value_at(lit, m_model) == l_true); + bool_var v = lit.var(); + TRACE("sat", tout << v << " := " << m_hscore[v] << "\n";); + unsigned_vector const& use1 = get_use(lit); + unsigned sz = use1.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned cl = use1[i]; + TRACE("sat", tout << *m_clauses[cl] << " " << m_num_true[cl] << "\n";); + SASSERT(m_num_true[cl] > 0); + if (m_num_true[cl] == 1) { + // num_true 0 -> 1 + // other literals don't have upside any more. + // subtract one from all other literals + adjust_all_values(lit, cl, -static_cast(m_clause_weights[cl])); + } + else if (m_num_true[cl] == 2) { + // num_true 1 -> 2, previous critical literal is no longer critical + adjust_pivot_value(lit, cl, +m_clause_weights[cl]); + } + } + unsigned_vector const& use2 = get_use(~lit); + sz = use2.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned cl = use2[i]; + TRACE("sat", tout << *m_clauses[cl] << " " << m_num_true[cl] << "\n";); + if (m_num_true[cl] == 0) { + // num_true 1 -> 0 + // all variables became critical. + adjust_all_values(~lit, cl, +m_clause_weights[cl]); + } + else if (m_num_true[cl] == 1) { + adjust_pivot_value(~lit, cl, -static_cast(m_clause_weights[cl])); + } + // else n+1 -> n >= 2 + } + } + + void wsls::adjust_all_values(literal lit, unsigned cl, int delta) { + clause const& c = *m_clauses[cl]; + unsigned sz = c.size(); + TRACE("sat", tout << lit << " " << c << " delta: " << delta << " nt: " << m_num_true[cl] << "\n";); + for (unsigned i = 0; i < sz; ++i) { + literal lit2 = c[i]; + if (lit2 != lit) { + TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); + m_hscore[lit2.var()] += delta; + TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); + refresh_scores(lit2.var()); + } + } + } + + void wsls::adjust_pivot_value(literal lit, unsigned cl, int delta) { + clause const& c = *m_clauses[cl]; + unsigned csz = c.size(); + for (unsigned j = 0; j < csz; ++j) { + literal lit2 = c[j]; + if (lit2 != lit && value_at(lit2, m_model) == l_true) { + TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); + m_hscore[lit2.var()] += delta; + TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); + refresh_scores(lit2.var()); + break; + } + } + } + + void wsls::refresh_scores(bool_var v) { + if (m_hscore[v] > 0 && !m_tabu[v] && m_sscore[v] == 0) { + m_H.insert(v); + } + else { + m_H.remove(v); + } + if (m_sscore[v] > 0) { + if (m_hscore[v] == 0 && !m_tabu[v]) { + m_S.insert(v); + } + else { + m_S.remove(v); + } + } + else if (m_sscore[v] < 0) { + m_S.remove(v); + } + } + + void wsls::check_invariant() { + sls::check_invariant(); + // The hscore is the reward for flipping the truth value of variable v. + // hscore(v) = Sum weight(c) for num_true(c) = 0 and v in c + // - Sum weight(c) for num_true(c) = 1 and (v in c, M(v) or !v in c and !M(v)) + for (unsigned v = 0; v < s.num_vars(); ++v) { + int hs = compute_hscore(v); + CTRACE("sat", hs != m_hscore[v], display(tout << v << " - computed: " << hs << " - assigned: " << m_hscore[v] << "\n");); + SASSERT(m_hscore[v] == hs); + } + + // The score(v) is the reward on soft clauses for flipping v. + for (unsigned j = 0; j < m_soft.size(); ++j) { + unsigned v = m_soft[j].var(); + double ss = (l_true == value_at(m_soft[j], m_model))?(-m_weights[j]):m_weights[j]; + SASSERT(m_sscore[v] == ss); + } + + // m_H are values such that m_hscore > 0 and sscore = 0. + for (bool_var v = 0; v < m_hscore.size(); ++v) { + SASSERT((m_hscore[v] > 0 && !m_tabu[v] && m_sscore[v] == 0) == m_H.contains(v)); + } + + // m_S are values such that hscore = 0, sscore > 0 + for (bool_var v = 0; v < m_sscore.size(); ++v) { + SASSERT((m_hscore[v] == 0 && m_sscore[v] > 0 && !m_tabu[v]) == m_S.contains(v)); + } + } + + void wsls::display(std::ostream& out) const { + sls::display(out); + out << "Best model\n"; + for (bool_var v = 0; v < m_best_model.size(); ++v) { + out << v << ": " << m_best_model[v] << " h: " << m_hscore[v]; + if (m_sscore[v] != 0.0) out << " s: " << m_sscore[v]; + out << "\n"; + } + } + +}; + diff --git a/src/sat/sat_sls.h b/src/sat/sat_sls.h new file mode 100644 index 000000000..7ea472083 --- /dev/null +++ b/src/sat/sat_sls.h @@ -0,0 +1,117 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sat_sls.h + +Abstract: + + SLS for clauses in SAT solver + +Author: + + Nikolaj Bjorner (nbjorner) 2014-12-8 + +Notes: + +--*/ +#ifndef SAT_SLS_H_ +#define SAT_SLS_H_ + +#include "util.h" +#include "sat_simplifier.h" + +namespace sat { + + class index_set { + unsigned_vector m_elems; + unsigned_vector m_index; + public: + unsigned num_elems() const { return m_elems.size(); } + unsigned operator[](unsigned idx) const { return m_elems[idx]; } + void reset() { m_elems.reset(); m_index.reset(); } + bool empty() const { return m_elems.empty(); } + bool contains(unsigned idx) const; + void insert(unsigned idx); + void remove(unsigned idx); + unsigned choose(random_gen& rnd) const; + }; + + class sls { + protected: + solver& s; + random_gen m_rand; + unsigned m_max_tries; + unsigned m_prob_choose_min_var; // number between 0 and 99. + unsigned m_clause_generation; + ptr_vector m_clauses; // vector of all clauses. + index_set m_false; // clauses currently false + vector m_use_list; // use lists for literals + unsigned_vector m_num_true; // per clause, count of # true literals + svector m_min_vars; // literals with smallest break count + model m_model; // current model + clause_allocator m_alloc; // clause allocator + clause_vector m_bin_clauses; // binary clauses + svector m_tabu; // variables that cannot be swapped + volatile bool m_cancel; + public: + sls(solver& s); + virtual ~sls(); + lbool operator()(unsigned sz, literal const* tabu, bool reuse_model); + void set_cancel(bool f) { m_cancel = f; } + void set_max_tries(unsigned mx) { m_max_tries = mx; } + virtual void display(std::ostream& out) const; + protected: + void init(unsigned sz, literal const* tabu, bool reuse_model); + void init_tabu(unsigned sz, literal const* tabu); + void init_model(); + void init_use(); + void init_clauses(); + unsigned_vector const& get_use(literal lit); + void flip(literal lit); + virtual void check_invariant(); + void check_use_list(); + private: + bool pick_flip(literal& lit); + void flip(); + unsigned get_break_count(literal lit, unsigned min_break); + }; + + /** + \brief sls with weighted soft clauses. + */ + class wsls : public sls { + unsigned_vector m_clause_weights; + svector m_hscore; + svector m_sscore; + literal_vector m_soft; + svector m_weights; + double m_best_value; + model m_best_model; + index_set m_H, m_S; + unsigned m_smoothing_probability; + public: + wsls(solver& s); + virtual ~wsls(); + void set_soft(unsigned sz, literal const* lits, double const* weights); + bool has_soft() const { return !m_soft.empty(); } + void opt(unsigned sz, literal const* tabu, bool reuse_model); + virtual void display(std::ostream& out) const; + double evaluate_model(model& mdl); + private: + void wflip(); + void wflip(literal lit); + void update_hard_weights(); + bool pick_wflip(literal & lit); + virtual void check_invariant(); + void refresh_scores(bool_var v); + int compute_hscore(bool_var v); + void recompute_hscores(literal lit); + void adjust_all_values(literal lit, unsigned cl, int delta); + void adjust_pivot_value(literal lit, unsigned cl, int delta); + }; + +}; + +#endif diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 5c0d60260..008ba8a18 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -20,6 +20,7 @@ Revision History: #include"sat_integrity_checker.h" #include"luby.h" #include"trace.h" +#include"sat_bceq.h" // define to update glue during propagation #define UPDATE_GLUE @@ -30,8 +31,9 @@ Revision History: namespace sat { - solver::solver(params_ref const & p, extension * ext): + solver::solver(params_ref const & p, reslimit& l, extension * ext): m_cancel(false), + m_rlimit(l), m_config(p), m_ext(ext), m_cleaner(*this), @@ -39,6 +41,8 @@ namespace sat { m_scc(*this, p), m_asymm_branch(*this, p), m_probing(*this, p), + m_mus(*this), + m_wsls(*this), m_inconsistent(false), m_num_frozen(0), m_activity_inc(128), @@ -47,6 +51,11 @@ namespace sat { m_scope_lvl(0), m_params(p) { updt_params(p); + m_conflicts_since_gc = 0; + m_conflicts = 0; + m_next_simplify = 0; + m_num_checkpoints = 0; + m_initializing_preferred = false; } solver::~solver() { @@ -61,6 +70,7 @@ namespace sat { for (clause * const * it = begin; it != end; ++it) { m_cls_allocator.del_clause(*it); } + ++m_stats.m_non_learned_generation; } void solver::copy(solver const & src) { @@ -88,7 +98,7 @@ namespace sat { if (!it2->is_binary_non_learned_clause()) continue; literal l2 = it2->get_literal(); - mk_clause(l, l2); + mk_clause_core(l, l2); } } } @@ -102,7 +112,7 @@ namespace sat { buffer.reset(); for (unsigned i = 0; i < c.size(); i++) buffer.push_back(c[i]); - mk_clause(buffer.size(), buffer.c_ptr()); + mk_clause_core(buffer); } } } @@ -143,7 +153,16 @@ namespace sat { for (unsigned i = 0; i < num_lits; i++) SASSERT(m_eliminated[lits[i].var()] == false); }); - mk_clause_core(num_lits, lits, false); + + if (m_user_scope_literals.empty()) { + mk_clause_core(num_lits, lits, false); + } + else { + m_aux_literals.reset(); + m_aux_literals.append(num_lits, lits); + m_aux_literals.append(m_user_scope_literals); + mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), false); + } } void solver::mk_clause(literal l1, literal l2) { @@ -156,15 +175,22 @@ namespace sat { mk_clause(3, ls); } + void solver::del_clause(clause& c) { + if (!c.is_learned()) m_stats.m_non_learned_generation++; + m_cls_allocator.del_clause(&c); + m_stats.m_del_clause++; + } + clause * solver::mk_clause_core(unsigned num_lits, literal * lits, bool learned) { + TRACE("sat", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << "\n";); if (!learned) { - TRACE("sat_mk_clause", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << "\n";); bool keep = simplify_clause(num_lits, lits); TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";); if (!keep) { return 0; // clause is equivalent to true. } - } + ++m_stats.m_non_learned_generation; + } switch (num_lits) { case 0: @@ -383,6 +409,7 @@ namespace sat { for (unsigned i = 1; i < num_lits; i++) { literal l = cls[i]; lbool val = value(l); + CTRACE("sat", val != l_false, tout << l << ":=" << val;); SASSERT(val == l_false); if (max_false_idx == UINT_MAX || lvl(l) > lvl(cls[max_false_idx])) max_false_idx = i; @@ -527,7 +554,7 @@ namespace sat { m_cleaner.dec(); SASSERT(!m_inconsistent); l = m_trail[m_qhead]; - TRACE("sat_propagate", tout << "propagating: " << l << "\n";); + TRACE("sat_propagate", tout << "propagating: " << l << " " << m_justification[l.var()] << "\n";); m_qhead++; not_l = ~l; SASSERT(value(l) == l_true); @@ -669,6 +696,7 @@ namespace sat { } wlist.set_end(it2); } + SASSERT(m_qhead == m_trail.size()); SASSERT(!m_inconsistent); return true; } @@ -685,7 +713,9 @@ namespace sat { // Search // // ----------------------- - lbool solver::check() { + lbool solver::check(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { + pop_to_base_level(); + IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); SASSERT(scope_lvl() == 0); #ifdef CLONE_BEFORE_SOLVING if (m_mc.empty()) { @@ -698,22 +728,24 @@ namespace sat { init_search(); propagate(false); if (inconsistent()) return l_false; + init_assumptions(num_lits, lits, weights, max_weight); + propagate(false); + if (check_inconsistent()) return l_false; cleanup(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; lbool r = bounded_search(); if (r != l_undef) return r; - pop(scope_lvl()); + pop_reinit(scope_lvl()); m_conflicts_since_restart = 0; m_restart_threshold = m_config.m_restart_initial; } - // iff3_finder(*this)(); + // iff3_finder(*this)(); simplify_problem(); - - if (inconsistent()) return l_false; - m_next_simplify = m_config.m_restart_initial * m_config.m_simplify_mult1; + if (check_inconsistent()) return l_false; + if (m_config.m_max_conflicts == 0) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = 0\"\n";); @@ -733,12 +765,8 @@ namespace sat { } restart(); - if (m_conflicts >= m_next_simplify) { - simplify_problem(); - m_next_simplify = static_cast(m_conflicts * m_config.m_simplify_mult2); - if (m_next_simplify > m_conflicts + m_config.m_simplify_max) - m_next_simplify = m_conflicts + m_config.m_simplify_max; - } + simplify_problem(); + if (check_inconsistent()) return l_false; gc(); } } @@ -803,7 +831,7 @@ namespace sat { SASSERT(phase != l_undef); literal next_lit(next, phase == l_false); assign(next_lit, justification()); - TRACE("sat_decide", tout << "next-case-split: " << next_lit << "\n";); + TRACE("sat_decide", tout << scope_lvl() << ": next-case-split: " << next_lit << "\n";); return true; } @@ -822,8 +850,10 @@ namespace sat { return l_undef; if (scope_lvl() == 0) { cleanup(); // cleaner may propagate frozen clauses - if (inconsistent()) + if (inconsistent()) { + TRACE("sat", tout << "conflict at level 0\n";); return l_false; + } gc(); } } @@ -850,26 +880,266 @@ namespace sat { } } + bool solver::check_inconsistent() { + if (inconsistent()) { + if (tracking_assumptions()) + resolve_conflict(); + return true; + } + else { + return false; + } + } + + void solver::init_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { + if (num_lits == 0 && m_user_scope_literals.empty()) { + return; + } + + retry_init_assumptions: + reset_assumptions(); + push(); + + propagate(false); + if (inconsistent()) { + return; + } + + TRACE("sat", + for (unsigned i = 0; i < num_lits; ++i) + tout << lits[i] << " "; + tout << "\n"; + if (!m_user_scope_literals.empty()) { + tout << "user literals: " << m_user_scope_literals << "\n"; + } + m_mc.display(tout); + ); + + for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { + literal nlit = ~m_user_scope_literals[i]; + assign(nlit, justification()); + } + + if (weights && !inconsistent()) { + if (m_config.m_optimize_model) { + m_wsls.set_soft(num_lits, lits, weights); + } + if (!init_weighted_assumptions(num_lits, lits, weights, max_weight)) { + goto retry_init_assumptions; + } + return; + } + + for (unsigned i = 0; !inconsistent() && i < num_lits; ++i) { + literal lit = lits[i]; + SASSERT(is_external(lit.var())); + add_assumption(lit); + assign(lit, justification()); + } + } + + + bool solver::init_weighted_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { + flet _min1(m_config.m_minimize_core, false); + m_weight = 0; + m_blocker.reset(); + svector values; + unsigned num_cores = 0; + for (unsigned i = 0; !inconsistent() && i < num_lits; ++i) { + literal lit = lits[i]; + SASSERT(is_external(lit.var())); + TRACE("sat", tout << "propagate: " << lit << " " << value(lit) << "\n";); + SASSERT(m_scope_lvl == 1); + add_assumption(lit); + switch(value(lit)) { + case l_undef: + values.push_back(l_true); + assign(lit, justification()); + if (num_cores*2 >= num_lits) { + break; + } + propagate(false); + if (inconsistent()) { + flet _init(m_initializing_preferred, true); + while (inconsistent()) { + if (!resolve_conflict()) { + return true; + } + propagate(true); + } + if (m_scope_lvl == 0) { + return false; + } + // backjump to last consistent assumption: + unsigned j; + m_weight = 0; + m_blocker.reset(); + for (j = 0; j < i && value(lits[j]) == values[j]; ++j) { + if (values[j] == l_false) { + m_weight += weights[j]; + m_blocker.push_back(lits[j]); + } + } + SASSERT(value(lits[j]) != values[j]); + SASSERT(j <= i); + SASSERT(j == 0 || value(lits[j-1]) == values[j-1]); + for (unsigned k = i; k >= j; --k) { + if (is_assumption(lits[k])) { + pop_assumption(); + } + } + values.resize(j); + TRACE("sat", tout << "backjump " << (i - j + 1) << " steps " << num_cores << "\n";); + i = j - 1; + } + break; + + case l_false: + ++num_cores; + values.push_back(l_false); + SASSERT(!inconsistent()); + set_conflict(justification(), ~lit); + m_conflict_lvl = scope_lvl(); + resolve_conflict_for_unsat_core(); + IF_VERBOSE(3, verbose_stream() << "core: " << m_core << "\n";); + update_min_core(); + SASSERT(m_min_core_valid); + m_weight += weights[i]; + if (m_weight <= max_weight) { + m_blocker.push_back(lit); + } + TRACE("sat", tout << "core: " << m_core << "\nassumptions: " << m_assumptions << "\n";); + SASSERT(m_core.size() <= m_assumptions.size()); + SASSERT(m_assumptions.size() <= i+1); + if (m_core.size() <= 3) { + m_inconsistent = true; + TRACE("opt", tout << "found small core: " << m_core << "\n";); + IF_VERBOSE(11, verbose_stream() << "small core: " << m_core << "\n";); + return true; + } + pop_assumption(); + m_inconsistent = false; + break; + case l_true: + values.push_back(l_true); + SASSERT(m_justification[lit.var()].get_kind() != justification::NONE || lvl(lit) == 0); + break; + } + } + TRACE("sat", tout << "initialized\n";); + IF_VERBOSE(11, verbose_stream() << "Blocker: " << m_blocker << "\nCore: " << m_min_core << "\n";); + if (m_weight >= max_weight) { + // block the current correction set candidate. + ++m_stats.m_blocked_corr_sets; + TRACE("opt", tout << "blocking soft correction set: " << m_blocker << "\n";); + IF_VERBOSE(11, verbose_stream() << "blocking " << m_blocker << "\n";); + pop_to_base_level(); + mk_clause_core(m_blocker); + return false; + } + return true; + } + + + + void solver::update_min_core() { + if (!m_min_core_valid || m_core.size() < m_min_core.size()) { + m_min_core.reset(); + m_min_core.append(m_core); + m_min_core_valid = true; + } + } + + void solver::reset_assumptions() { + m_assumptions.reset(); + m_assumption_set.reset(); + } + + void solver::add_assumption(literal lit) { + m_assumption_set.insert(lit); + m_assumptions.push_back(lit); + } + + void solver::pop_assumption() { + VERIFY(m_assumptions.back() == m_assumption_set.pop()); + m_assumptions.pop_back(); + } + + void solver::reassert_min_core() { + SASSERT(m_min_core_valid); + pop_to_base_level(); + push(); + reset_assumptions(); + TRACE("sat", tout << "reassert: " << m_min_core << "\n";); + for (unsigned i = 0; i < m_min_core.size(); ++i) { + literal lit = m_min_core[i]; + SASSERT(is_external(lit.var())); + add_assumption(lit); + assign(lit, justification()); + } + propagate(false); + SASSERT(inconsistent()); + } + + void solver::reinit_assumptions() { + if (tracking_assumptions() && scope_lvl() == 0) { + TRACE("sat", tout << m_assumptions << "\n";); + push(); + for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { + assign(~m_user_scope_literals[i], justification()); + } + for (unsigned i = 0; !inconsistent() && i < m_assumptions.size(); ++i) { + assign(m_assumptions[i], justification()); + } + } + } + + bool solver::tracking_assumptions() const { + return !m_assumptions.empty() || !m_user_scope_literals.empty(); + } + + bool solver::is_assumption(literal l) const { + return tracking_assumptions() && m_assumption_set.contains(l); + } + void solver::init_search() { + m_model_is_current = false; m_phase_counter = 0; m_phase_cache_on = false; - m_conflicts = 0; m_conflicts_since_restart = 0; m_restart_threshold = m_config.m_restart_initial; m_luby_idx = 1; - m_conflicts_since_gc = 0; m_gc_threshold = m_config.m_gc_initial; m_min_d_tk = 1.0; - m_next_simplify = 0; m_stopwatch.reset(); m_stopwatch.start(); + m_core.reset(); + m_min_core_valid = false; + m_min_core.reset(); TRACE("sat", display(tout);); + + if (m_config.m_bcd) { + bceq bc(*this); + bc(); + } } /** \brief Apply all simplifications. + */ void solver::simplify_problem() { + if (m_conflicts < m_next_simplify) { + return; + } + IF_VERBOSE(2, verbose_stream() << "(sat.simplify)\n";); + + // Disable simplification during MUS computation. + // if (m_mus.is_active()) return; + TRACE("sat", tout << "simplify\n";); + + pop(scope_lvl()); + SASSERT(scope_lvl() == 0); m_cleaner(); @@ -881,7 +1151,7 @@ namespace sat { m_simplifier(false); CASSERT("sat_simplify_bug", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); - + if (!m_learned.empty()) { m_simplifier(true); CASSERT("sat_missed_prop", check_missed_propagation()); @@ -903,6 +1173,19 @@ namespace sat { m_ext->clauses_modifed(); m_ext->simplify(); } + + TRACE("sat", display(tout << "consistent: " << (!inconsistent()) << "\n");); + + reinit_assumptions(); + + if (m_next_simplify == 0) { + m_next_simplify = m_config.m_restart_initial * m_config.m_simplify_mult1; + } + else { + m_next_simplify = static_cast(m_conflicts * m_config.m_simplify_mult2); + if (m_next_simplify > m_conflicts + m_config.m_simplify_max) + m_next_simplify = m_conflicts + m_config.m_simplify_max; + } } void solver::sort_watch_lits() { @@ -914,8 +1197,15 @@ namespace sat { } } + void solver::set_model(model const& mdl) { + m_model.reset(); + m_model.append(mdl); + m_model_is_current = !m_model.empty(); + } + void solver::mk_model() { m_model.reset(); + m_model_is_current = true; unsigned num = num_vars(); m_model.resize(num, l_undef); for (bool_var v = 0; v < num; v++) { @@ -923,13 +1213,17 @@ namespace sat { m_model[v] = value(v); } TRACE("sat_mc_bug", m_mc.display(tout);); + if (m_config.m_optimize_model) { + m_wsls.opt(0, 0, false); + } m_mc(m_model); - TRACE("sat_model", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); + TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); #ifndef _EXTERNAL_RELEASE IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model\"\n";); if (!check_model(m_model)) throw solver_exception("check model failed"); + if (m_clone) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model (on original set of clauses)\"\n";); if (!m_clone->check_model(m_model)) @@ -948,7 +1242,12 @@ namespace sat { for (; it != end; ++it) { clause const & c = *(*it); if (!c.satisfied_by(m)) { - TRACE("sat_model_bug", tout << "failed: " << c << "\n";); + TRACE("sat", tout << "failed: " << c << "\n"; + tout << "assumptions: " << m_assumptions << "\n"; + tout << "trail: " << m_trail << "\n"; + tout << "model: " << m << "\n"; + m_mc.display(tout); + ); ok = false; } } @@ -966,15 +1265,28 @@ namespace sat { continue; literal l2 = it2->get_literal(); if (value_at(l2, m) != l_true) { - TRACE("sat_model_bug", tout << "failed binary: " << l << " " << l2 << " learned: " << it2->is_learned() << "\n";); + TRACE("sat", tout << "failed binary: " << l << " " << l2 << " learned: " << it2->is_learned() << "\n"; + m_mc.display(tout);); ok = false; } } } } - if (!m_mc.check_model(m)) + for (unsigned i = 0; i < m_assumptions.size(); ++i) { + if (value_at(m_assumptions[i], m) != l_true) { + TRACE("sat", + tout << m_assumptions[i] << " does not model check\n"; + tout << "trail: " << m_trail << "\n"; + tout << "model: " << m << "\n"; + m_mc.display(tout); + ); + ok = false; + } + } + if (ok && !m_mc.check_model(m)) { ok = false; - CTRACE("sat_model_bug", !ok, tout << m << "\n";); + TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); + } return ok; } @@ -985,7 +1297,7 @@ namespace sat { << " :restarts " << m_stats.m_restart << mk_stat(*this) << " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";); IF_VERBOSE(30, display_status(verbose_stream());); - pop(scope_lvl()); + pop_reinit(scope_lvl()); m_conflicts_since_restart = 0; switch (m_config.m_restart) { case RS_GEOMETRIC: @@ -1260,7 +1572,7 @@ namespace sat { break; } } - TRACE("sat_gc", tout << "after cleanup:\n" << mk_lits_pp(j, c.begin()) << "\n";); + TRACE("sat", tout << "after cleanup:\n" << mk_lits_pp(j, c.begin()) << "\n";); unsigned new_sz = j; switch (new_sz) { case 0: @@ -1318,7 +1630,6 @@ namespace sat { } bool solver::resolve_conflict_core() { - TRACE("sat_conflict", tout << "conflict detected\n";); m_stats.m_conflict++; m_conflicts++; @@ -1326,8 +1637,23 @@ namespace sat { m_conflicts_since_gc++; m_conflict_lvl = get_max_lvl(m_not_l, m_conflict); - if (m_conflict_lvl == 0) + TRACE("sat", tout << "conflict detected at level " << m_conflict_lvl << " for "; + if (m_not_l == literal()) tout << "null literal\n"; + else tout << m_not_l << "\n";); + + if (m_initializing_preferred) { + SASSERT(m_conflict_lvl <= 1); + return resolve_conflict_for_init(); + } + if (m_conflict_lvl <= 1 && tracking_assumptions()) { + resolve_conflict_for_unsat_core(); return false; + } + + if (m_conflict_lvl == 0) { + return false; + } + m_lemma.reset(); forget_phase_of_vars(m_conflict_lvl); @@ -1347,7 +1673,7 @@ namespace sat { do { TRACE("sat_conflict_detail", tout << "processing consequent: " << consequent << "\n"; - tout << "num_marks: " << num_marks << ", js kind: " << js.get_kind() << "\n";); + tout << "num_marks: " << num_marks << ", js: " << js << "\n";); switch (js.get_kind()) { case justification::NONE: break; @@ -1431,7 +1757,7 @@ namespace sat { unsigned glue = num_diff_levels(m_lemma.size(), m_lemma.c_ptr()); - pop(m_scope_lvl - new_scope_lvl); + pop_reinit(m_scope_lvl - new_scope_lvl); TRACE("sat_conflict_detail", display(tout); tout << "assignment:\n"; display_assignment(tout);); clause * lemma = mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); if (lemma) { @@ -1442,6 +1768,272 @@ namespace sat { return true; } + void solver::process_antecedent_for_unsat_core(literal antecedent) { + bool_var var = antecedent.var(); + SASSERT(var < num_vars()); + TRACE("sat", tout << antecedent << " " << (is_marked(var)?"+":"-") << "\n";); + if (!is_marked(var)) { + mark(var); + m_unmark.push_back(var); + if (is_assumption(antecedent)) { + m_core.push_back(antecedent); + } + } + } + + void solver::process_consequent_for_unsat_core(literal consequent, justification const& js) { + TRACE("sat", tout << "processing consequent: "; + if (consequent == null_literal) tout << "null\n"; + else tout << consequent << "\n"; + display_justification(tout << "js kind: ", js); + tout << "\n";); + switch (js.get_kind()) { + case justification::NONE: + break; + case justification::BINARY: + SASSERT(consequent != null_literal); + process_antecedent_for_unsat_core(~(js.get_literal())); + break; + case justification::TERNARY: + SASSERT(consequent != null_literal); + process_antecedent_for_unsat_core(~(js.get_literal1())); + process_antecedent_for_unsat_core(~(js.get_literal2())); + break; + case justification::CLAUSE: { + clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + unsigned i = 0; + if (consequent != null_literal) { + SASSERT(c[0] == consequent || c[1] == consequent); + if (c[0] == consequent) { + i = 1; + } + else { + process_antecedent_for_unsat_core(~c[0]); + i = 2; + } + } + unsigned sz = c.size(); + for (; i < sz; i++) + process_antecedent_for_unsat_core(~c[i]); + break; + } + case justification::EXT_JUSTIFICATION: { + fill_ext_antecedents(consequent, js); + literal_vector::iterator it = m_ext_antecedents.begin(); + literal_vector::iterator end = m_ext_antecedents.end(); + for (; it != end; ++it) + process_antecedent_for_unsat_core(*it); + break; + } + default: + UNREACHABLE(); + break; + } + } + + bool solver::resolve_conflict_for_init() { + if (m_conflict_lvl == 0) { + return false; + } + m_lemma.reset(); + m_lemma.push_back(null_literal); // asserted literal + if (m_not_l != null_literal) { + TRACE("sat", tout << "not_l: " << m_not_l << "\n";); + process_antecedent_for_init(m_not_l); + } + literal consequent = m_not_l; + justification js = m_conflict; + + SASSERT(m_trail.size() > 0); + unsigned idx = m_trail.size(); + while (process_consequent_for_init(consequent, js)) { + while (true) { + --idx; + literal l = m_trail[idx]; + if (is_marked(l.var())) + break; + SASSERT(idx > 0); + } + consequent = m_trail[idx]; + bool_var c_var = consequent.var(); + if (lvl(consequent) == 0) { + return false; + } + SASSERT(m_conflict_lvl == 1); + js = m_justification[c_var]; + reset_mark(c_var); + } + + unsigned new_scope_lvl = 0; + m_lemma[0] = ~consequent; + for (unsigned i = 1; i < m_lemma.size(); ++i) { + bool_var var = m_lemma[i].var(); + if (is_marked(var)) { + reset_mark(var); + new_scope_lvl = std::max(new_scope_lvl, lvl(var)); + } + else { + m_lemma[i] = m_lemma.back(); + m_lemma.pop_back(); + --i; + } + } + TRACE("sat", tout << "lemma: " << m_lemma << "\n"; display(tout); tout << "assignment:\n"; display_assignment(tout);); + if (new_scope_lvl == 0) { + pop_reinit(m_scope_lvl); + } + else { + unassign_vars(idx); + } + mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); + TRACE("sat", tout << "Trail: " << m_trail << "\n";); + m_inconsistent = false; + return true; + } + + bool solver::process_consequent_for_init(literal consequent, justification const& js) { + switch (js.get_kind()) { + case justification::NONE: + return false; + case justification::BINARY: + process_antecedent_for_init(~(js.get_literal())); + break; + case justification::TERNARY: + process_antecedent_for_init(~(js.get_literal1())); + process_antecedent_for_init(~(js.get_literal2())); + break; + case justification::CLAUSE: { + clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + unsigned i = 0; + if (consequent != null_literal) { + SASSERT(c[0] == consequent || c[1] == consequent); + if (c[0] == consequent) { + i = 1; + } + else { + process_antecedent_for_init(~c[0]); + i = 2; + } + } + unsigned sz = c.size(); + for (; i < sz; i++) + process_antecedent_for_init(~c[i]); + break; + } + case justification::EXT_JUSTIFICATION: { + fill_ext_antecedents(consequent, js); + literal_vector::iterator it = m_ext_antecedents.begin(); + literal_vector::iterator end = m_ext_antecedents.end(); + for (; it != end; ++it) + process_antecedent_for_init(*it); + break; + } + default: + UNREACHABLE(); + break; + } + return true; + } + + void solver::process_antecedent_for_init(literal antecedent) { + bool_var var = antecedent.var(); + SASSERT(var < num_vars()); + if (!is_marked(var) && lvl(var) > 0) { + mark(var); + m_lemma.push_back(~antecedent); + } + } + + + void solver::resolve_conflict_for_unsat_core() { + TRACE("sat", display(tout); + unsigned level = 0; + for (unsigned i = 0; i < m_trail.size(); ++i) { + literal l = m_trail[i]; + if (level != m_level[l.var()]) { + level = m_level[l.var()]; + tout << level << ": "; + } + tout << l; + if (m_mark[l.var()]) { + tout << "*"; + } + tout << " "; + } + tout << "\n"; + ); + + m_core.reset(); + if (m_conflict_lvl == 0) { + return; + } + SASSERT(m_unmark.empty()); + DEBUG_CODE({ + for (unsigned i = 0; i < m_trail.size(); ++i) { + SASSERT(!is_marked(m_trail[i].var())); + }}); + + unsigned old_size = m_unmark.size(); + int idx = skip_literals_above_conflict_level(); + + if (m_not_l != null_literal) { + justification js = m_justification[m_not_l.var()]; + TRACE("sat", tout << "not_l: " << m_not_l << "\n"; + display_justification(tout, js); + tout << "\n";); + + process_antecedent_for_unsat_core(m_not_l); + if (is_assumption(~m_not_l)) { + m_core.push_back(~m_not_l); + } + else { + process_consequent_for_unsat_core(m_not_l, js); + } + } + + literal consequent = m_not_l; + justification js = m_conflict; + + + while (true) { + process_consequent_for_unsat_core(consequent, js); + while (idx >= 0) { + literal l = m_trail[idx]; + if (is_marked(l.var())) + break; + idx--; + } + + if (idx < 0) { + break; + } + consequent = m_trail[idx]; + if (lvl(consequent) < m_conflict_lvl) { + TRACE("sat", tout << consequent << " at level " << lvl(consequent) << "\n";); + break; + } + bool_var c_var = consequent.var(); + SASSERT(lvl(consequent) == m_conflict_lvl); + js = m_justification[c_var]; + idx--; + } + reset_unmark(old_size); + if (m_config.m_minimize_core) { + if (m_min_core_valid && m_min_core.size() < m_core.size()) { + IF_VERBOSE(1, verbose_stream() << "(sat.updating core " << m_min_core.size() << " " << m_core.size() << ")\n";); + m_core.reset(); + m_core.append(m_min_core); + } + // TBD: + // apply optional clause minimization by detecting subsumed literals. + // initial experiment suggests it has no effect. + m_mus(); // ignore return value on cancelation. + set_model(m_mus.get_model()); + IF_VERBOSE(2, verbose_stream() << "(sat.core: " << m_core << ")\n";); + } + } + + unsigned solver::get_max_lvl(literal consequent, justification js) { if (!m_ext) return scope_lvl(); @@ -1530,6 +2122,7 @@ namespace sat { } } + /** \brief js is an external justification. Collect its antecedents and store at m_ext_antecedents. */ @@ -1717,7 +2310,8 @@ namespace sat { assigned at level 0. */ void solver::minimize_lemma() { - m_unmark.reset(); + SASSERT(m_unmark.empty()); + //m_unmark.reset(); updt_lemma_lvl_set(); unsigned sz = m_lemma.size(); @@ -1726,6 +2320,7 @@ namespace sat { for (; i < sz; i++) { literal l = m_lemma[i]; if (implied_by_marked(l)) { + TRACE("sat", tout << "drop: " << l << "\n";); m_unmark.push_back(l.var()); } else { @@ -1887,6 +2482,7 @@ namespace sat { // ----------------------- void solver::push() { SASSERT(!inconsistent()); + TRACE("sat_verbose", tout << "q:" << m_qhead << " trail: " << m_trail.size() << "\n";); SASSERT(m_qhead == m_trail.size()); m_scopes.push_back(scope()); scope & s = m_scopes.back(); @@ -1898,6 +2494,11 @@ namespace sat { m_ext->push(); } + void solver::pop_reinit(unsigned num_scopes) { + pop(num_scopes); + reinit_assumptions(); + } + void solver::pop(unsigned num_scopes) { if (num_scopes == 0) return; @@ -1962,6 +2563,133 @@ namespace sat { m_clauses_to_reinit.shrink(j); } + // + // All new clauses that are added to the solver + // are relative to the user-scope literals. + // + + void solver::user_push() { + literal lit; + bool_var new_v = mk_var(true, false); + lit = literal(new_v, false); + m_user_scope_literals.push_back(lit); + TRACE("sat", tout << "user_push: " << lit << "\n";); + } + + void solver::gc_lit(clause_vector &clauses, literal lit) { + unsigned j = 0; + for (unsigned i = 0; i < clauses.size(); ++i) { + clause & c = *(clauses[i]); + if (c.contains(lit)) { + dettach_clause(c); + del_clause(c); + } + else { + clauses[j] = &c; + ++j; + } + } + clauses.shrink(j); + } + + void solver::gc_bin(bool learned, literal nlit) { + m_user_bin_clauses.reset(); + collect_bin_clauses(m_user_bin_clauses, learned); + for (unsigned i = 0; i < m_user_bin_clauses.size(); ++i) { + literal l1 = m_user_bin_clauses[i].first; + literal l2 = m_user_bin_clauses[i].second; + if (nlit == l1 || nlit == l2) { + dettach_bin_clause(l1, l2, learned); + } + } + } + + bool_var solver::max_var(bool learned, bool_var v) { + m_user_bin_clauses.reset(); + collect_bin_clauses(m_user_bin_clauses, learned); + for (unsigned i = 0; i < m_user_bin_clauses.size(); ++i) { + literal l1 = m_user_bin_clauses[i].first; + literal l2 = m_user_bin_clauses[i].second; + if (l1.var() > v) v = l1.var(); + if (l2.var() > v) v = l2.var(); + } + return v; + } + + bool_var solver::max_var(clause_vector& clauses, bool_var v) { + for (unsigned i = 0; i < clauses.size(); ++i) { + clause & c = *(clauses[i]); + literal* it = c.begin(); + literal * end = c.end(); + for (; it != end; ++it) { + if (it->var() > v) { + v = it->var(); + } + } + } + return v; + } + + void solver::gc_var(bool_var v) { + if (v > 0) { + bool_var w = max_var(m_learned, v-1); + w = max_var(m_clauses, w); + w = max_var(true, w); + w = max_var(false, w); + for (unsigned i = 0; i < m_trail.size(); ++i) { + if (m_trail[i].var() > w) w = m_trail[i].var(); + } + v = std::max(v, w + 1); + } + // v is an index of a variable that does not occur in solver state. + if (v < m_level.size()) { + for (bool_var i = v; i < m_level.size(); ++i) { + m_case_split_queue.del_var_eh(i); + } + m_watches.shrink(2*v); + m_assignment.shrink(2*v); + m_justification.shrink(v); + m_decision.shrink(v); + m_eliminated.shrink(v); + m_external.shrink(v); + m_activity.shrink(v); + m_level.shrink(v); + m_mark.shrink(v); + m_lit_mark.shrink(2*v); + m_phase.shrink(v); + m_prev_phase.shrink(v); + m_assigned_since_gc.shrink(v); + m_simplifier.reset_todo(); + } + } + + void solver::user_pop(unsigned num_scopes) { + pop_to_base_level(); + while (num_scopes > 0) { + literal lit = m_user_scope_literals.back(); + m_user_scope_literals.pop_back(); + gc_lit(m_learned, lit); + gc_lit(m_clauses, lit); + gc_bin(true, lit); + gc_bin(false, lit); + TRACE("sat", tout << "gc: " << lit << "\n"; display(tout);); + --num_scopes; + for (unsigned i = 0; i < m_trail.size(); ++i) { + if (m_trail[i] == lit) { + TRACE("sat", tout << m_trail << "\n";); + unassign_vars(i); + break; + } + } + gc_var(lit.var()); + } + } + + void solver::pop_to_base_level() { + reset_assumptions(); + pop(scope_lvl()); + } + // ----------------------- // // Misc @@ -1988,9 +2716,10 @@ namespace sat { void solver::set_cancel(bool f) { m_cancel = f; + m_wsls.set_cancel(f); } - void solver::collect_statistics(statistics & st) { + void solver::collect_statistics(statistics & st) const { m_stats.collect_statistics(st); m_cleaner.collect_statistics(st); m_simplifier.collect_statistics(st); @@ -2055,6 +2784,7 @@ namespace sat { // // ----------------------- bool solver::check_invariant() const { + if (m_cancel) return true; integrity_checker checker(*this); SASSERT(checker()); return true; @@ -2103,6 +2833,13 @@ namespace sat { out << ")\n"; } + void solver::display_justification(std::ostream & out, justification const& js) const { + out << js; + if (js.is_clause()) { + out << *(m_cls_allocator.get_clause(js.get_clause_offset())); + } + } + unsigned solver::num_clauses() const { unsigned num_cls = 0; num_cls += m_trail.size(); // units; @@ -2158,6 +2895,50 @@ namespace sat { } } + void solver::display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const { + unsigned max_weight = 0; + for (unsigned i = 0; i < sz; ++i) { + max_weight = std::max(max_weight, weights[i]); + } + ++max_weight; + + out << "p wcnf " << num_vars() << " " << num_clauses() + sz << " " << max_weight << "\n"; + + for (unsigned i = 0; i < m_trail.size(); i++) { + out << max_weight << " " << dimacs_lit(m_trail[i]) << " 0\n"; + } + vector::const_iterator it = m_watches.begin(); + vector::const_iterator end = m_watches.end(); + for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { + literal l = ~to_literal(l_idx); + watch_list const & wlist = *it; + watch_list::const_iterator it2 = wlist.begin(); + watch_list::const_iterator end2 = wlist.end(); + for (; it2 != end2; ++it2) { + if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) + out << max_weight << " " << dimacs_lit(l) << " " << dimacs_lit(it2->get_literal()) << " 0\n"; + } + } + clause_vector const * vs[2] = { &m_clauses, &m_learned }; + for (unsigned i = 0; i < 2; i++) { + clause_vector const & cs = *(vs[i]); + clause_vector::const_iterator it = cs.begin(); + clause_vector::const_iterator end = cs.end(); + for (; it != end; ++it) { + clause const & c = *(*it); + unsigned sz = c.size(); + out << max_weight << " "; + for (unsigned j = 0; j < sz; j++) + out << dimacs_lit(c[j]) << " "; + out << "0\n"; + } + } + for (unsigned i = 0; i < sz; ++i) { + out << weights[i] << " " << lits[i] << " 0\n"; + } + } + + void solver::display_watches(std::ostream & out) const { vector::const_iterator it = m_watches.begin(); vector::const_iterator end = m_watches.end(); @@ -2171,9 +2952,7 @@ namespace sat { } void solver::display_assignment(std::ostream & out) const { - for (unsigned i = 0; i < m_trail.size(); i++) - out << m_trail[i] << " "; - out << "\n"; + out << m_trail << "\n"; } /** @@ -2357,6 +3136,7 @@ namespace sat { st.update("restarts", m_restart); st.update("minimized lits", m_minimized_lits); st.update("dyn subsumption resolution", m_dyn_sub_res); + st.update("blocked correction sets", m_blocked_corr_sets); } void stats::reset() { @@ -2374,6 +3154,8 @@ namespace sat { m_del_clause = 0; m_minimized_lits = 0; m_dyn_sub_res = 0; + m_non_learned_generation = 0; + m_blocked_corr_sets = 0; } void mk_stat::display(std::ostream & out) const { diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 72f1d5770..23033d940 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_SOLVER_H_ -#define _SAT_SOLVER_H_ +#ifndef SAT_SOLVER_H_ +#define SAT_SOLVER_H_ #include"sat_types.h" #include"sat_clause.h" @@ -32,10 +32,13 @@ Revision History: #include"sat_asymm_branch.h" #include"sat_iff3_finder.h" #include"sat_probing.h" +#include"sat_mus.h" +#include"sat_sls.h" #include"params.h" #include"statistics.h" #include"stopwatch.h" #include"trace.h" +#include"rlimit.h" namespace sat { @@ -57,6 +60,8 @@ namespace sat { unsigned m_del_clause; unsigned m_minimized_lits; unsigned m_dyn_sub_res; + unsigned m_non_learned_generation; + unsigned m_blocked_corr_sets; stats() { reset(); } void reset(); void collect_statistics(statistics & st) const; @@ -67,18 +72,22 @@ namespace sat { struct abort_solver {}; protected: volatile bool m_cancel; + reslimit& m_rlimit; config m_config; stats m_stats; extension * m_ext; random_gen m_rand; clause_allocator m_cls_allocator; cleaner m_cleaner; - model m_model; + model m_model; model_converter m_mc; + bool m_model_is_current; simplifier m_simplifier; scc m_scc; asymm_branch m_asymm_branch; probing m_probing; + mus m_mus; // MUS for minimal core extraction + wsls m_wsls; // SLS facility for MaxSAT use bool m_inconsistent; // A conflict is usually a single justification. That is, a justification // for false. If m_not_l is not null_literal, then m_conflict is a @@ -118,6 +127,9 @@ namespace sat { stopwatch m_stopwatch; params_ref m_params; scoped_ptr m_clone; // for debugging purposes + literal_vector m_assumptions; // additional assumptions during check + literal_set m_assumption_set; // set of enabled assumptions + literal_vector m_core; // unsat core void del_clauses(clause * const * begin, clause * const * end); @@ -129,9 +141,13 @@ namespace sat { friend class asymm_branch; friend class probing; friend class iff3_finder; + friend class mus; + friend class sls; + friend class wsls; + friend class bceq; friend struct mk_stat; public: - solver(params_ref const & p, extension * ext); + solver(params_ref const & p, reslimit& l, extension * ext); ~solver(); // ----------------------- @@ -143,7 +159,7 @@ namespace sat { static void collect_param_descrs(param_descrs & d); void set_cancel(bool f); - void collect_statistics(statistics & st); + void collect_statistics(statistics & st) const; void reset_statistics(); void display_status(std::ostream & out) const; @@ -161,13 +177,17 @@ namespace sat { // // ----------------------- bool_var mk_var(bool ext = false, bool dvar = true); + void mk_clause(literal_vector const& lits) { mk_clause(lits.size(), lits.c_ptr()); } void mk_clause(unsigned num_lits, literal * lits); void mk_clause(literal l1, literal l2); void mk_clause(literal l1, literal l2, literal l3); protected: - void del_clause(clause & c) { m_cls_allocator.del_clause(&c); m_stats.m_del_clause++; } + void del_clause(clause & c); clause * mk_clause_core(unsigned num_lits, literal * lits, bool learned); + void mk_clause_core(literal_vector const& lits) { mk_clause_core(lits.size(), lits.c_ptr()); } + void mk_clause_core(unsigned num_lits, literal * lits) { mk_clause_core(num_lits, lits, false); } + void mk_clause_core(literal l1, literal l2) { literal lits[2] = { l1, l2 }; mk_clause_core(2, lits); } void mk_bin_clause(literal l1, literal l2, bool learned); bool propagate_bin_clause(literal l1, literal l2); clause * mk_ter_clause(literal * lits, bool learned); @@ -197,6 +217,7 @@ namespace sat { public: bool inconsistent() const { return m_inconsistent; } unsigned num_vars() const { return m_level.size(); } + unsigned num_clauses() const; bool is_external(bool_var v) const { return m_external[v] != 0; } bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } unsigned scope_lvl() const { return m_scope_lvl; } @@ -215,12 +236,17 @@ namespace sat { void assign_core(literal l, justification jst); void set_conflict(justification c, literal not_l); void set_conflict(justification c) { set_conflict(c, null_literal); } - lbool status(clause const & c) const; + lbool status(clause const & c) const; clause_offset get_offset(clause const & c) const { return m_cls_allocator.get_offset(&c); } void checkpoint() { if (m_cancel) throw solver_exception(Z3_CANCELED_MSG); + if (!m_rlimit.inc()) { m_cancel = true; throw solver_exception(Z3_CANCELED_MSG); } + ++m_num_checkpoints; + if (m_num_checkpoints < 10) return; + m_num_checkpoints = 0; if (memory::get_allocation_size() > m_config.m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } + typedef std::pair bin_clause; protected: watch_list & get_wlist(literal l) { return m_watches[l.index()]; } watch_list const & get_wlist(literal l) const { return m_watches[l.index()]; } @@ -231,6 +257,8 @@ namespace sat { bool is_marked_lit(literal l) const { return m_lit_mark[l.index()] != 0; } void mark_lit(literal l) { SASSERT(!is_marked_lit(l)); m_lit_mark[l.index()] = true; } void unmark_lit(literal l) { SASSERT(is_marked_lit(l)); m_lit_mark[l.index()] = false; } + bool check_inconsistent(); + // ----------------------- // @@ -250,9 +278,16 @@ namespace sat { // // ----------------------- public: - lbool check(); + lbool check(unsigned num_lits = 0, literal const* lits = 0) { + return check(num_lits, lits, 0, 0); + } + lbool check(unsigned num_lits, literal const* lits, double const* weights, double max_weight); + model const & get_model() const { return m_model; } + bool model_is_current() const { return m_model_is_current; } + literal_vector const& get_core() const { return m_core; } model_converter const & get_model_converter() const { return m_mc; } + void set_model(model const& mdl); protected: unsigned m_conflicts; @@ -261,12 +296,30 @@ namespace sat { unsigned m_luby_idx; unsigned m_conflicts_since_gc; unsigned m_gc_threshold; + unsigned m_num_checkpoints; double m_min_d_tk; unsigned m_next_simplify; bool decide(); bool_var next_var(); lbool bounded_search(); void init_search(); + + literal_vector m_min_core; + bool m_min_core_valid; + literal_vector m_blocker; + double m_weight; + bool m_initializing_preferred; + void init_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight); + bool init_weighted_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight); + void reassert_min_core(); + void update_min_core(); + void resolve_weighted(); + void reset_assumptions(); + void add_assumption(literal lit); + void pop_assumption(); + void reinit_assumptions(); + bool tracking_assumptions() const; + bool is_assumption(literal l) const; void simplify_problem(); void mk_model(); bool check_model(model const & m) const; @@ -314,6 +367,12 @@ namespace sat { bool resolve_conflict_core(); unsigned get_max_lvl(literal consequent, justification js); void process_antecedent(literal antecedent, unsigned & num_marks); + void resolve_conflict_for_unsat_core(); + void process_antecedent_for_unsat_core(literal antecedent); + void process_consequent_for_unsat_core(literal consequent, justification const& js); + bool resolve_conflict_for_init(); + void process_antecedent_for_init(literal antecedent); + bool process_consequent_for_init(literal consequent, justification const& js); void fill_ext_antecedents(literal consequent, justification js); unsigned skip_literals_above_conflict_level(); void forget_phase_of_vars(unsigned from_lvl); @@ -339,14 +398,27 @@ namespace sat { // Backtracking // // ----------------------- - public: void push(); void pop(unsigned num_scopes); + void pop_reinit(unsigned num_scopes); - protected: void unassign_vars(unsigned old_sz); void reinit_clauses(unsigned old_sz); + literal_vector m_user_scope_literals; + literal_vector m_aux_literals; + svector m_user_bin_clauses; + void gc_lit(clause_vector& clauses, literal lit); + void gc_bin(bool learned, literal nlit); + void gc_var(bool_var v); + bool_var max_var(clause_vector& clauses, bool_var v); + bool_var max_var(bool learned, bool_var v); + + public: + void user_push(); + void user_pop(unsigned num_scopes); + void pop_to_base_level(); + reslimit& rlimit() { return m_rlimit; } // ----------------------- // // Simplification @@ -390,7 +462,6 @@ namespace sat { clause * const * end_clauses() const { return m_clauses.end(); } clause * const * begin_learned() const { return m_learned.begin(); } clause * const * end_learned() const { return m_learned.end(); } - typedef std::pair bin_clause; void collect_bin_clauses(svector & r, bool learned) const; // ----------------------- @@ -403,12 +474,13 @@ namespace sat { void display(std::ostream & out) const; void display_watches(std::ostream & out) const; void display_dimacs(std::ostream & out) const; + void display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const; + void display_assignment(std::ostream & out) const; + void display_justification(std::ostream & out, justification const& j) const; protected: void display_binary(std::ostream & out) const; - void display_units(std::ostream & out) const; - void display_assignment(std::ostream & out) const; - unsigned num_clauses() const; + void display_units(std::ostream & out) const; bool is_unit(clause const & c) const; bool is_empty(clause const & c) const; bool check_missed_propagation(clause_vector const & cs) const; diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp new file mode 100644 index 000000000..fd1f4cfd0 --- /dev/null +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -0,0 +1,442 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + inc_sat_solver.cpp + +Abstract: + + incremental solver based on SAT core. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-7-30 + +Notes: + +--*/ + +#include "solver.h" +#include "tactical.h" +#include "sat_solver.h" +#include "tactic2solver.h" +#include "aig_tactic.h" +#include "propagate_values_tactic.h" +#include "max_bv_sharing_tactic.h" +#include "card2bv_tactic.h" +#include "bit_blaster_tactic.h" +#include "simplify_tactic.h" +#include "goal2sat.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" +#include "filter_model_converter.h" +#include "bit_blaster_model_converter.h" + +// incremental SAT solver. +class inc_sat_solver : public solver { + ast_manager& m; + sat::solver m_solver; + goal2sat m_goal2sat; + params_ref m_params; + bool m_optimize_model; // parameter + expr_ref_vector m_fmls; + expr_ref_vector m_asmsf; + unsigned_vector m_fmls_lim; + unsigned_vector m_asms_lim; + unsigned_vector m_fmls_head_lim; + unsigned m_fmls_head; + expr_ref_vector m_core; + atom2bool_var m_map; + model_ref m_model; + model_converter_ref m_mc; + bit_blaster_rewriter m_bb_rewriter; + tactic_ref m_preprocess; + unsigned m_num_scopes; + sat::literal_vector m_asms; + goal_ref_buffer m_subgoals; + proof_converter_ref m_pc; + model_converter_ref m_mc2; + expr_dependency_ref m_dep_core; + svector m_weights; + + typedef obj_map dep2asm_t; +public: + inc_sat_solver(ast_manager& m, params_ref const& p): + m(m), m_solver(p, m.limit(), 0), + m_params(p), m_optimize_model(false), + m_fmls(m), + m_asmsf(m), + m_fmls_head(0), + m_core(m), + m_map(m), + m_bb_rewriter(m, p), + m_num_scopes(0), + m_dep_core(m) { + m_params.set_bool("elim_vars", false); + m_solver.updt_params(m_params); + params_ref simp2_p = p; + simp2_p.set_bool("som", true); + simp2_p.set_bool("pull_cheap_ite", true); + simp2_p.set_bool("push_ite_bv", false); + simp2_p.set_bool("local_ctx", true); + simp2_p.set_uint("local_ctx_limit", 10000000); + simp2_p.set_bool("flat", true); // required by som + simp2_p.set_bool("hoist_mul", false); // required by som + m_preprocess = + and_then(mk_card2bv_tactic(m, m_params), + using_params(mk_simplify_tactic(m), simp2_p), + mk_max_bv_sharing_tactic(m), + mk_bit_blaster_tactic(m, &m_bb_rewriter), + mk_aig_tactic(), + using_params(mk_simplify_tactic(m), simp2_p)); + } + + virtual ~inc_sat_solver() {} + + virtual void set_progress_callback(progress_callback * callback) {} + + virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) { + return check_sat(num_assumptions, assumptions, 0, 0); + } + + void display_weighted(std::ostream& out, unsigned sz, expr * const * assumptions, unsigned const* weights) { + m_weights.reset(); + if (weights != 0) { + for (unsigned i = 0; i < sz; ++i) m_weights.push_back(weights[i]); + } + m_solver.pop_to_base_level(); + dep2asm_t dep2asm; + VERIFY(l_true == internalize_formulas()); + VERIFY(l_true == internalize_assumptions(sz, assumptions, dep2asm)); + svector nweights; + for (unsigned i = 0; i < m_asms.size(); ++i) { + nweights.push_back((unsigned) m_weights[i]); + } + m_solver.display_wcnf(out, m_asms.size(), m_asms.c_ptr(), nweights.c_ptr()); + } + + lbool check_sat(unsigned sz, expr * const * assumptions, double const* weights, double max_weight) { + m_weights.reset(); + if (weights != 0) { + m_weights.append(sz, weights); + } + SASSERT(m_weights.empty() == (m_weights.c_ptr() == 0)); + m_solver.pop_to_base_level(); + dep2asm_t dep2asm; + m_model = 0; + lbool r = internalize_formulas(); + if (r != l_true) return r; + r = internalize_assumptions(sz, assumptions, dep2asm); + if (r != l_true) return r; + + r = m_solver.check(m_asms.size(), m_asms.c_ptr(), m_weights.c_ptr(), max_weight); + switch (r) { + case l_true: + if (sz > 0 && !weights) { + check_assumptions(dep2asm); + } + break; + case l_false: + // TBD: expr_dependency core is not accounted for. + if (sz > 0) { + extract_core(dep2asm); + } + break; + default: + break; + } + return r; + } + virtual void set_cancel(bool f) { + m_goal2sat.set_cancel(f); + m_solver.set_cancel(f); + if (f) m_preprocess->cancel(); else m_preprocess->reset_cancel(); + } + virtual void push() { + internalize_formulas(); + m_solver.user_push(); + ++m_num_scopes; + m_fmls_lim.push_back(m_fmls.size()); + m_asms_lim.push_back(m_asmsf.size()); + m_fmls_head_lim.push_back(m_fmls_head); + m_bb_rewriter.push(); + m_map.push(); + } + virtual void pop(unsigned n) { + if (n < m_num_scopes) { // allow inc_sat_solver to + n = m_num_scopes; // take over for another solver. + } + m_bb_rewriter.pop(n); + m_map.pop(n); + SASSERT(n >= m_num_scopes); + m_solver.user_pop(n); + m_num_scopes -= n; + while (n > 0) { + m_fmls_head = m_fmls_head_lim.back(); + m_fmls.resize(m_fmls_lim.back()); + m_fmls_lim.pop_back(); + m_fmls_head_lim.pop_back(); + m_asmsf.resize(m_asms_lim.back()); + m_asms_lim.pop_back(); + --n; + } + } + virtual unsigned get_scope_level() const { + return m_num_scopes; + } + virtual void assert_expr(expr * t, expr * a) { + if (a) { + m_asmsf.push_back(a); + assert_expr(m.mk_implies(a, t)); + } + else { + assert_expr(t); + } + } + virtual void assert_expr(expr * t) { + TRACE("sat", tout << mk_pp(t, m) << "\n";); + m_fmls.push_back(t); + } + virtual void set_produce_models(bool f) {} + virtual void collect_param_descrs(param_descrs & r) { + goal2sat::collect_param_descrs(r); + sat::solver::collect_param_descrs(r); + } + virtual void updt_params(params_ref const & p) { + m_params = p; + m_params.set_bool("elim_vars", false); + m_solver.updt_params(m_params); + m_optimize_model = m_params.get_bool("optimize_model", false); + } + virtual void collect_statistics(statistics & st) const { + m_preprocess->collect_statistics(st); + m_solver.collect_statistics(st); + } + virtual void get_unsat_core(ptr_vector & r) { + r.reset(); + r.append(m_core.size(), m_core.c_ptr()); + } + virtual void get_model(model_ref & mdl) { + if (!m_model.get()) { + extract_model(); + } + mdl = m_model; + } + virtual proof * get_proof() { + UNREACHABLE(); + return 0; + } + virtual std::string reason_unknown() const { + return "no reason given"; + } + virtual void get_labels(svector & r) { + UNREACHABLE(); + } + virtual unsigned get_num_assertions() const { + return m_fmls.size(); + } + virtual expr * get_assertion(unsigned idx) const { + return m_fmls[idx]; + } + virtual unsigned get_num_assumptions() const { + return m_asmsf.size(); + } + virtual expr * get_assumption(unsigned idx) const { + return m_asmsf[idx]; + } + +private: + + + lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm) { + m_mc2.reset(); + m_pc.reset(); + m_dep_core.reset(); + m_subgoals.reset(); + m_preprocess->reset(); + SASSERT(g->models_enabled()); + SASSERT(!g->proofs_enabled()); + TRACE("sat", g->display(tout);); + try { + (*m_preprocess)(g, m_subgoals, m_mc2, m_pc, m_dep_core); + } + catch (tactic_exception & ex) { + IF_VERBOSE(0, verbose_stream() << "exception in tactic " << ex.msg() << "\n";); + return l_undef; + } + if (m_subgoals.size() != 1) { + IF_VERBOSE(0, verbose_stream() << "size of subgoals is not 1, it is: " << m_subgoals.size() << "\n";); + return l_undef; + } + CTRACE("sat", m_mc.get(), m_mc->display(tout); ); + g = m_subgoals[0]; + TRACE("sat", g->display_with_dependencies(tout);); + m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, true); + return l_true; + } + + lbool internalize_assumptions(unsigned sz, expr* const* asms, dep2asm_t& dep2asm) { + if (sz == 0) { + return l_true; + } + goal_ref g = alloc(goal, m, true, true); // models and cores are enabled. + for (unsigned i = 0; i < sz; ++i) { + g->assert_expr(asms[i], m.mk_leaf(asms[i])); + } + lbool res = internalize_goal(g, dep2asm); + if (res == l_true) { + extract_assumptions(sz, asms, dep2asm); + } + return res; + } + + lbool internalize_formulas() { + if (m_fmls_head == m_fmls.size()) { + return l_true; + } + dep2asm_t dep2asm; + goal_ref g = alloc(goal, m, true, false); // models, maybe cores are enabled + for (; m_fmls_head < m_fmls.size(); ++m_fmls_head) { + g->assert_expr(m_fmls[m_fmls_head].get()); + } + return internalize_goal(g, dep2asm); + } + + void extract_assumptions(unsigned sz, expr* const* asms, dep2asm_t& dep2asm) { + m_asms.reset(); + unsigned j = 0; + sat::literal lit; + for (unsigned i = 0; i < sz; ++i) { + if (dep2asm.find(asms[i], lit)) { + m_asms.push_back(lit); + if (i != j && !m_weights.empty()) { + m_weights[j] = m_weights[i]; + } + ++j; + } + } + SASSERT(dep2asm.size() == m_asms.size()); + } + + void extract_core(dep2asm_t& dep2asm) { + u_map asm2dep; + dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); + for (; it != end; ++it) { + expr* e = it->m_key; + asm2dep.insert(it->m_value.index(), e); + } + sat::literal_vector const& core = m_solver.get_core(); + TRACE("sat", + dep2asm_t::iterator it2 = dep2asm.begin(); + dep2asm_t::iterator end2 = dep2asm.end(); + for (; it2 != end2; ++it2) { + tout << mk_pp(it2->m_key, m) << " |-> " << sat::literal(it2->m_value) << "\n"; + } + tout << "core: "; + for (unsigned i = 0; i < core.size(); ++i) { + tout << core[i] << " "; + } + tout << "\n"; + ); + + m_core.reset(); + for (unsigned i = 0; i < core.size(); ++i) { + expr* e; + VERIFY(asm2dep.find(core[i].index(), e)); + m_core.push_back(e); + } + } + + void check_assumptions(dep2asm_t& dep2asm) { + sat::model const & ll_m = m_solver.get_model(); + dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); + for (; it != end; ++it) { + sat::literal lit = it->m_value; + if (sat::value_at(lit, ll_m) != l_true) { + IF_VERBOSE(0, verbose_stream() << mk_pp(it->m_key, m) << " does not evaluate to true\n"; + verbose_stream() << m_asms << "\n"; + m_solver.display_assignment(verbose_stream()); + m_solver.display(verbose_stream());); + throw default_exception("bad state"); + } + } + } + + void extract_model() { + TRACE("sat", tout << "retrieve model\n";); + if (!m_solver.model_is_current()) { + m_model = 0; + return; + } + sat::model const & ll_m = m_solver.get_model(); + model_ref md = alloc(model, m); + atom2bool_var::iterator it = m_map.begin(); + atom2bool_var::iterator end = m_map.end(); + for (; it != end; ++it) { + expr * n = it->m_key; + if (is_app(n) && to_app(n)->get_num_args() > 0) { + continue; + } + sat::bool_var v = it->m_value; + switch (sat::value_at(v, ll_m)) { + case l_true: + md->register_decl(to_app(n)->get_decl(), m.mk_true()); + break; + case l_false: + md->register_decl(to_app(n)->get_decl(), m.mk_false()); + break; + default: + break; + } + } + m_model = md; + if (m_mc || !m_bb_rewriter.const2bits().empty()) { + model_converter_ref mc = m_mc; + if (!m_bb_rewriter.const2bits().empty()) { + mc = concat(mc.get(), mk_bit_blaster_model_converter(m, m_bb_rewriter.const2bits())); + } + (*mc)(m_model); + } + SASSERT(m_model); + + DEBUG_CODE( + for (unsigned i = 0; i < m_fmls.size(); ++i) { + expr_ref tmp(m); + VERIFY(m_model->eval(m_fmls[i].get(), tmp)); + CTRACE("sat", !m.is_true(tmp), + tout << "Evaluation failed: " << mk_pp(m_fmls[i].get(), m) + << " to " << tmp << "\n"; + model_smt2_pp(tout, m, *(m_model.get()), 0);); + SASSERT(m.is_true(tmp)); + }); + } +}; + + +solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p) { + return alloc(inc_sat_solver, m, p); +} + + +lbool inc_sat_check_sat(solver& _s, unsigned sz, expr*const* soft, rational const* _weights, rational const& max_weight) { + inc_sat_solver& s = dynamic_cast(_s); + vector weights; + for (unsigned i = 0; _weights && i < sz; ++i) { + weights.push_back(_weights[i].get_double()); + } + return s.check_sat(sz, soft, weights.c_ptr(), max_weight.get_double()); +} + +void inc_sat_display(std::ostream& out, solver& _s, unsigned sz, expr*const* soft, rational const* _weights) { + inc_sat_solver& s = dynamic_cast(_s); + vector weights; + for (unsigned i = 0; _weights && i < sz; ++i) { + if (!_weights[i].is_unsigned()) { + throw default_exception("Cannot display weights that are not integers"); + } + weights.push_back(_weights[i].get_unsigned()); + } + return s.display_weighted(out, sz, soft, weights.c_ptr()); +} + diff --git a/src/sat/sat_solver/inc_sat_solver.h b/src/sat/sat_solver/inc_sat_solver.h new file mode 100644 index 000000000..028f71b06 --- /dev/null +++ b/src/sat/sat_solver/inc_sat_solver.h @@ -0,0 +1,31 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + inc_sat_solver.h + +Abstract: + + incremental solver based on SAT core. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-7-30 + +Notes: + +--*/ + +#ifndef HS_INC_SAT_SOLVER_H_ +#define HS_INC_SAT_SOLVER_H_ + +#include "solver.h" + +solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p); + +lbool inc_sat_check_sat(solver& s, unsigned sz, expr*const* soft, rational const* weights, rational const& max_weight); + +void inc_sat_display(std::ostream& out, solver& s, unsigned sz, expr*const* soft, rational const* _weights); + +#endif diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index db8de94a9..c0c087e59 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_TYPES_H_ -#define _SAT_TYPES_H_ +#ifndef SAT_TYPES_H_ +#define SAT_TYPES_H_ #include"debug.h" #include"approx_set.h" @@ -160,6 +160,12 @@ namespace sat { m_in_set[v] = true; m_set.push_back(v); } + + uint_set& operator=(uint_set const& other) { + m_in_set = other.m_in_set; + m_set = other.m_set; + return *this; + } bool contains(unsigned v) const { return v < m_in_set.size() && m_in_set[v] != 0; @@ -177,10 +183,31 @@ namespace sat { m_in_set[v] = false; return v; } + unsigned size() const { return m_set.size(); } iterator begin() const { return m_set.begin(); } iterator end() const { return m_set.end(); } void reset() { m_set.reset(); m_in_set.reset(); } void cleanup() { m_set.finalize(); m_in_set.finalize(); } + uint_set& operator&=(uint_set const& other) { + unsigned j = 0; + for (unsigned i = 0; i < m_set.size(); ++i) { + if (other.contains(m_set[i])) { + m_set[j] = m_set[i]; + ++j; + } + else { + m_in_set[m_set[i]] = false; + } + } + m_set.resize(j); + return *this; + } + uint_set& operator|=(uint_set const& other) { + for (unsigned i = 0; i < other.m_set.size(); ++i) { + insert(other.m_set[i]); + } + return *this; + } }; typedef uint_set bool_var_set; @@ -188,11 +215,45 @@ namespace sat { class literal_set { uint_set m_set; public: + literal_set(literal_vector const& v) { + for (unsigned i = 0; i < v.size(); ++i) insert(v[i]); + } + literal_set() {} + literal_vector to_vector() const { + literal_vector result; + iterator it = begin(), e = end(); + for (; it != e; ++it) { + result.push_back(*it); + } + return result; + } void insert(literal l) { m_set.insert(l.index()); } + literal pop() { return to_literal(m_set.erase()); } bool contains(literal l) const { return m_set.contains(l.index()); } bool empty() const { return m_set.empty(); } + unsigned size() const { return m_set.size(); } void reset() { m_set.reset(); } void cleanup() { m_set.cleanup(); } + class iterator { + uint_set::iterator m_it; + public: + iterator(uint_set::iterator it):m_it(it) {} + literal operator*() const { return to_literal(*m_it); } + iterator& operator++() { ++m_it; return *this; } + iterator operator++(int) { iterator tmp = *this; ++m_it; return tmp; } + bool operator==(iterator const& it) const { return m_it == it.m_it; } + bool operator!=(iterator const& it) const { return m_it != it.m_it; } + }; + iterator begin() const { return iterator(m_set.begin()); } + iterator end() const { return iterator(m_set.end()); } + literal_set& operator&=(literal_set const& other) { + m_set &= other.m_set; + return *this; + } + literal_set& operator|=(literal_set const& other) { + m_set |= other.m_set; + return *this; + } }; struct mem_stat { diff --git a/src/sat/sat_var_queue.h b/src/sat/sat_var_queue.h index 24d5f57a0..f008fbb88 100644 --- a/src/sat/sat_var_queue.h +++ b/src/sat/sat_var_queue.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_VAR_QUEUE_H_ -#define _SAT_VAR_QUEUE_H_ +#ifndef SAT_VAR_QUEUE_H_ +#define SAT_VAR_QUEUE_H_ #include"heap.h" #include"sat_types.h" diff --git a/src/sat/sat_watched.h b/src/sat/sat_watched.h index 83c04e918..2c48b6c9b 100644 --- a/src/sat/sat_watched.h +++ b/src/sat/sat_watched.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SAT_WATCHED_H_ -#define _SAT_WATCHED_H_ +#ifndef SAT_WATCHED_H_ +#define SAT_WATCHED_H_ #include"sat_types.h" #include"vector.h" diff --git a/src/sat/tactic/atom2bool_var.cpp b/src/sat/tactic/atom2bool_var.cpp index 935eeb28e..48ad85152 100644 --- a/src/sat/tactic/atom2bool_var.cpp +++ b/src/sat/tactic/atom2bool_var.cpp @@ -90,8 +90,22 @@ struct collect_boolean_interface_proc { template void operator()(T const & g) { unsigned sz = g.size(); - for (unsigned i = 0; i < sz; i++) + ptr_vector deps, all_deps; + for (unsigned i = 0; i < sz; i++) { + if (g.dep(i)) { + deps.reset(); + m.linearize(g.dep(i), deps); + all_deps.append(deps); + } + } + + for (unsigned i = 0; i < all_deps.size(); i++) { + quick_for_each_expr(proc, tvisited, all_deps[i]); + } + for (unsigned i = 0; i < sz; i++) { process(g.form(i)); + } + } void operator()(unsigned sz, expr * const * fs) { diff --git a/src/sat/tactic/atom2bool_var.h b/src/sat/tactic/atom2bool_var.h index 4d6ce79e0..3435a1e2c 100644 --- a/src/sat/tactic/atom2bool_var.h +++ b/src/sat/tactic/atom2bool_var.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _ATOM2BOOL_VAR_H_ -#define _ATOM2BOOL_VAR_H_ +#ifndef ATOM2BOOL_VAR_H_ +#define ATOM2BOOL_VAR_H_ #include"expr2var.h" #include"sat_types.h" diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 16184e2bc..9f066e33b 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -35,6 +35,8 @@ Notes: #include"for_each_expr.h" #include"model_v2_pp.h" #include"tactic.h" +#include"ast_pp.h" +#include struct goal2sat::imp { struct frame { @@ -52,15 +54,21 @@ struct goal2sat::imp { obj_hashtable m_interface_vars; sat::solver & m_solver; atom2bool_var & m_map; + dep2asm_map & m_dep2asm; sat::bool_var m_true; bool m_ite_extra; unsigned long long m_max_memory; volatile bool m_cancel; + expr_ref_vector m_trail; + bool m_default_external; - imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map): + imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): m(_m), m_solver(s), - m_map(map) { + m_map(map), + m_dep2asm(dep2asm), + m_trail(m), + m_default_external(default_external) { updt_params(p); m_cancel = false; m_true = sat::null_bool_var; @@ -71,8 +79,9 @@ struct goal2sat::imp { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); } - void throw_op_not_handled() { - throw tactic_exception("operator not supported, apply simplifier before invoking translator"); + void throw_op_not_handled(std::string const& s) { + std::string s0 = "operator " + s + " not supported, apply simplifier before invoking translator"; + throw tactic_exception(s0.c_str()); } void mk_clause(sat::literal l) { @@ -96,14 +105,14 @@ struct goal2sat::imp { } sat::bool_var mk_true() { - // create fake variable to represent true; if (m_true == sat::null_bool_var) { + // create fake variable to represent true; m_true = m_solver.mk_var(); mk_clause(sat::literal(m_true, false)); // v is true } return m_true; } - + void convert_atom(expr * t, bool root, bool sign) { SASSERT(m.is_bool(t)); sat::literal l; @@ -116,7 +125,7 @@ struct goal2sat::imp { l = sat::literal(mk_true(), !sign); } else { - bool ext = !is_uninterp_const(t) || m_interface_vars.contains(t); + bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t); sat::bool_var v = m_solver.mk_var(ext); m_map.insert(t, v); l = sat::literal(v, sign); @@ -176,9 +185,12 @@ struct goal2sat::imp { case OP_AND: case OP_XOR: case OP_IMPLIES: - case OP_DISTINCT: + case OP_DISTINCT: { TRACE("goal2sat_not_handled", tout << mk_ismt2_pp(t, m) << "\n";); - throw_op_not_handled(); + std::ostringstream strm; + strm << mk_ismt2_pp(t, m); + throw_op_not_handled(strm.str()); + } default: convert_atom(t, root, sign); return true; @@ -360,15 +372,65 @@ struct goal2sat::imp { SASSERT(m_result_stack.empty()); } + void insert_dep(expr* dep0, expr* dep, bool sign) { + SASSERT(sign || dep0 == dep); // !sign || (not dep0) == dep. + SASSERT(!sign || m.is_not(dep0)); + expr_ref new_dep(m), fml(m); + if (is_uninterp_const(dep)) { + new_dep = dep; + } + else { + new_dep = m.mk_fresh_const("dep", m.mk_bool_sort()); + m_trail.push_back(new_dep); + m_interface_vars.insert(new_dep); + fml = m.mk_iff(new_dep, dep); + process(fml); + } + convert_atom(new_dep, false, false); + sat::literal lit = m_result_stack.back(); + m_dep2asm.insert(dep0, sign?~lit:lit); + m_result_stack.pop_back(); + } void operator()(goal const & g) { m_interface_vars.reset(); collect_boolean_interface(g, m_interface_vars); - unsigned size = g.size(); + expr_ref f(m), d_new(m); + ptr_vector deps; + expr_ref_vector fmls(m); for (unsigned idx = 0; idx < size; idx++) { - expr * f = g.form(idx); + f = g.form(idx); + // Add assumptions. + if (g.dep(idx)) { + deps.reset(); + fmls.reset(); + m.linearize(g.dep(idx), deps); + fmls.push_back(f); + for (unsigned i = 0; i < deps.size(); ++i) { + expr * d = deps[i]; + expr * d1 = d; + SASSERT(m.is_bool(d)); + bool sign = m.is_not(d, d1); + + insert_dep(d, d1, sign); + if (d == f) { + goto skip_dep; + } + if (sign) { + d_new = d1; + } + else { + d_new = m.mk_not(d); + } + fmls.push_back(d_new); + } + f = m.mk_or(fmls.size(), fmls.c_ptr()); + } + TRACE("sat", tout << mk_pp(f, m) << "\n";); process(f); + skip_dep: + ; } } @@ -439,8 +501,8 @@ struct goal2sat::scoped_set_imp { } }; -void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m) { - imp proc(g.m(), p, t, m); +void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external) { + imp proc(g.m(), p, t, m, dep2asm, default_external); scoped_set_imp set(this, &proc); proc(g); } @@ -453,6 +515,7 @@ void goal2sat::set_cancel(bool f) { } } + struct sat2goal::imp { // Wrapper for sat::model_converter: converts it into an "AST level" model_converter. diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index 7e38a1ca5..e1f78d916 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -26,8 +26,8 @@ Author: Notes: --*/ -#ifndef _GOAL2SAT_H_ -#define _GOAL2SAT_H_ +#ifndef GOAL2SAT_H_ +#define GOAL2SAT_H_ #include"goal.h" #include"sat_solver.h" @@ -41,6 +41,8 @@ class goal2sat { public: goal2sat(); + typedef obj_map dep2asm_map; + static void collect_param_descrs(param_descrs & r); static bool has_unsupported_bool(goal const & s); @@ -55,9 +57,10 @@ public: \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), an unsupported operator is found, or memory consumption limit is reached (set with param :max-memory). */ - void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m); + void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false); void set_cancel(bool f); + }; diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index 677540991..f04100ca2 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -34,7 +34,7 @@ class sat_tactic : public tactic { imp(ast_manager & _m, params_ref const & p): m(_m), - m_solver(p, 0), + m_solver(p, m.limit(), 0), m_params(p) { SASSERT(!m.proofs_enabled()); } @@ -46,13 +46,15 @@ class sat_tactic : public tactic { expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; fail_if_proof_generation("sat", g); - fail_if_unsat_core_generation("sat", g); bool produce_models = g->models_enabled(); + bool produce_core = g->unsat_core_enabled(); TRACE("before_sat_solver", g->display(tout);); g->elim_redundancies(); atom2bool_var map(m); - m_goal2sat(*g, m_params, m_solver, map); + obj_map dep2asm; + sat::literal_vector assumptions; + m_goal2sat(*g, m_params, m_solver, map, dep2asm); TRACE("sat_solver_unknown", tout << "interpreted_atoms: " << map.interpreted_atoms() << "\n"; atom2bool_var::iterator it = map.begin(); atom2bool_var::iterator end = map.end(); @@ -66,10 +68,21 @@ class sat_tactic : public tactic { CASSERT("sat_solver", m_solver.check_invariant()); IF_VERBOSE(TACTIC_VERBOSITY_LVL, m_solver.display_status(verbose_stream());); TRACE("sat_dimacs", m_solver.display_dimacs(tout);); - - lbool r = m_solver.check(); + dep2assumptions(dep2asm, assumptions); + lbool r = m_solver.check(assumptions.size(), assumptions.c_ptr()); if (r == l_false) { - g->assert_expr(m.mk_false(), 0, 0); + expr_dependency * lcore = 0; + if (produce_core) { + sat::literal_vector const& core = m_solver.get_core(); + u_map asm2dep; + mk_asm2dep(dep2asm, asm2dep); + for (unsigned i = 0; i < core.size(); ++i) { + sat::literal lit = core[i]; + expr* dep = asm2dep.find(lit.index()); + lcore = m.mk_join(lcore, m.mk_leaf(dep)); + } + } + g->assert_expr(m.mk_false(), 0, lcore); } else if (r == l_true && !map.interpreted_atoms()) { // register model @@ -103,7 +116,7 @@ class sat_tactic : public tactic { #if 0 IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "\"formula constains interpreted atoms, recovering formula from sat solver...\"\n";); #endif - m_solver.pop(m_solver.scope_lvl()); + m_solver.pop_to_base_level(); m_sat2goal(m_solver, map, m_params, *(g.get()), mc); } g->inc_depth(); @@ -115,6 +128,22 @@ class sat_tactic : public tactic { m_sat2goal.set_cancel(f); m_solver.set_cancel(f); } + + void dep2assumptions(obj_map& dep2asm, + sat::literal_vector& assumptions) { + obj_map::iterator it = dep2asm.begin(), end = dep2asm.end(); + for (; it != end; ++it) { + assumptions.push_back(it->m_value); + } + } + + void mk_asm2dep(obj_map& dep2asm, + u_map& lit2asm) { + obj_map::iterator it = dep2asm.begin(), end = dep2asm.end(); + for (; it != end; ++it) { + lit2asm.insert(it->m_value.index(), it->m_key); + } + } }; struct scoped_set_imp { diff --git a/src/sat/tactic/sat_tactic.h b/src/sat/tactic/sat_tactic.h index fcffaa494..115b01a71 100644 --- a/src/sat/tactic/sat_tactic.h +++ b/src/sat/tactic/sat_tactic.h @@ -16,8 +16,8 @@ Author: Notes: --*/ -#ifndef _SAT_TACTIC_H_ -#define _SAT_TACTIC_H_ +#ifndef SAT_TACTIC_H_ +#define SAT_TACTIC_H_ #include"params.h" class ast_manager; diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index a487a99b4..e802112b2 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -77,7 +77,7 @@ static void display_statistics( out << "--------------\n"; out << "instructions \n"; - code.display(*ctx.get_rel_context(), out); + code.display(ex_ctx, out); out << "--------------\n"; out << "big relations \n"; @@ -190,7 +190,7 @@ unsigned read_datalog(char const * file) { datalog::compiler::compile(ctx, ctx.get_rules(), rules_code, termination_code); - TRACE("dl_compiler", rules_code.display(*ctx.get_rel_context(), tout);); + TRACE("dl_compiler", rules_code.display(ex_ctx, tout);); rules_code.make_annotations(ex_ctx); @@ -230,7 +230,7 @@ unsigned read_datalog(char const * file) { TRACE("dl_compiler", ctx.display(tout); - rules_code.display(*ctx.get_rel_context(), tout);); + rules_code.display(ex_ctx, tout);); if (ctx.output_tuples()) { ctx.get_rel_context()->display_output_facts(ctx.get_rules(), std::cout); diff --git a/src/shell/datalog_frontend.h b/src/shell/datalog_frontend.h index 8022d5046..48ccb6c3d 100644 --- a/src/shell/datalog_frontend.h +++ b/src/shell/datalog_frontend.h @@ -16,12 +16,12 @@ Author: Revision History: --*/ -#ifndef _DATALOG_FRONTEND_H_ -#define _DATALOG_FRONTEND_H_ +#ifndef DATALOG_FRONTEND_H_ +#define DATALOG_FRONTEND_H_ unsigned read_datalog(char const * file); -#endif /* _DATALOG_FRONTEND_H_ */ +#endif /* DATALOG_FRONTEND_H_ */ diff --git a/src/shell/dimacs_frontend.cpp b/src/shell/dimacs_frontend.cpp index 0acd222dd..54e78ac8f 100644 --- a/src/shell/dimacs_frontend.cpp +++ b/src/shell/dimacs_frontend.cpp @@ -20,8 +20,10 @@ Revision History: #include #include #include"timeout.h" +#include"rlimit.h" #include"dimacs.h" #include"sat_solver.h" +#include"gparams.h" extern bool g_display_statistics; static sat::solver * g_solver = 0; @@ -63,13 +65,76 @@ static void display_model(sat::solver const & s) { std::cout << "\n"; } +static void display_core(sat::solver const& s, vector const& tracking_clauses) { + std::cout << "core\n"; + sat::literal_vector const& c = s.get_core(); + for (unsigned i = 0; i < c.size(); ++i) { + sat::literal_vector const& cls = tracking_clauses[c[i].var()]; + for (unsigned j = 0; j < cls.size(); ++j) { + std::cout << cls[j] << " "; + } + std::cout << "\n"; + } +} + +static void track_clause(sat::solver& dst, + sat::literal_vector& lits, + sat::literal_vector& assumptions, + vector& tracking_clauses) { + sat::literal lit = sat::literal(dst.mk_var(true, false), false); + tracking_clauses.set(lit.var(), lits); + lits.push_back(~lit); + dst.mk_clause(lits.size(), lits.c_ptr()); + assumptions.push_back(lit); +} + + +static void track_clauses(sat::solver const& src, + sat::solver& dst, + sat::literal_vector& assumptions, + vector& tracking_clauses) { + for (sat::bool_var v = 0; v < src.num_vars(); ++v) { + dst.mk_var(false, true); + } + sat::literal_vector lits; + sat::literal lit; + sat::clause * const * it = src.begin_clauses(); + sat::clause * const * end = src.end_clauses(); + svector bin_clauses; + src.collect_bin_clauses(bin_clauses, false); + tracking_clauses.reserve(2*src.num_vars() + static_cast(end - it) + bin_clauses.size()); + + for (sat::bool_var v = 1; v < src.num_vars(); ++v) { + if (src.value(v) != l_undef) { + bool sign = src.value(v) == l_false; + lits.reset(); + lits.push_back(sat::literal(v, sign)); + track_clause(dst, lits, assumptions, tracking_clauses); + } + } + for (; it != end; ++it) { + lits.reset(); + sat::clause& cls = *(*it); + lits.append(static_cast(cls.end()-cls.begin()), cls.begin()); + track_clause(dst, lits, assumptions, tracking_clauses); + } + for (unsigned i = 0; i < bin_clauses.size(); ++i) { + lits.reset(); + lits.push_back(bin_clauses[i].first); + lits.push_back(bin_clauses[i].second); + track_clause(dst, lits, assumptions, tracking_clauses); + } +} + + unsigned read_dimacs(char const * file_name) { g_start_time = clock(); register_on_timeout_proc(on_timeout); signal(SIGINT, on_ctrl_c); - params_ref p; + params_ref p = gparams::get_module("sat"); p.set_bool("produce_models", true); - sat::solver solver(p, 0); + reslimit limit; + sat::solver solver(p, limit, 0); g_solver = &solver; if (file_name) { @@ -85,17 +150,31 @@ unsigned read_dimacs(char const * file_name) { } IF_VERBOSE(20, solver.display_status(verbose_stream());); - lbool r = solver.check(); + lbool r; + vector tracking_clauses; + sat::solver solver2(p, limit, 0); + if (p.get_bool("dimacs.core", false)) { + g_solver = &solver2; + sat::literal_vector assumptions; + track_clauses(solver, solver2, assumptions, tracking_clauses); + r = g_solver->check(assumptions.size(), assumptions.c_ptr()); + } + else { + r = g_solver->check(); + } switch (r) { case l_true: std::cout << "sat\n"; - display_model(solver); + display_model(*g_solver); break; case l_undef: std::cout << "unknown\n"; break; case l_false: std::cout << "unsat\n"; + if (p.get_bool("dimacs.core", false)) { + display_core(*g_solver, tracking_clauses); + } break; } if (g_display_statistics) diff --git a/src/shell/dimacs_frontend.h b/src/shell/dimacs_frontend.h index 9521c8256..263643c32 100644 --- a/src/shell/dimacs_frontend.h +++ b/src/shell/dimacs_frontend.h @@ -16,10 +16,10 @@ Author: Revision History: --*/ -#ifndef _DIMACS_FRONTEND_H_ -#define _DIMACS_FRONTEND_H_ +#ifndef DIMACS_FRONTEND_H_ +#define DIMACS_FRONTEND_H_ unsigned read_dimacs(char const * benchmark_file); -#endif /* _DATALOG_FRONTEND_H_ */ +#endif /* DIMACS_FRONTEND_H_ */ diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 7769b1a27..609f5d47b 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -29,13 +29,14 @@ Revision History: #include"version.h" #include"dimacs_frontend.h" #include"datalog_frontend.h" +#include"opt_frontend.h" #include"timeout.h" #include"z3_exception.h" #include"error_codes.h" #include"gparams.h" #include"env_params.h" -typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_Z3_LOG } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG } input_kind; std::string g_aux_input_file; char const * g_input_file = 0; @@ -162,12 +163,21 @@ void parse_cmd_line_args(int argc, char ** argv) { else if (strcmp(opt_name, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } + else if (strcmp(opt_name, "dl") == 0) { + g_input_kind = IN_DATALOG; + } else if (strcmp(opt_name, "in") == 0) { g_standard_input = true; } else if (strcmp(opt_name, "dimacs") == 0) { g_input_kind = IN_DIMACS; } + else if (strcmp(opt_name, "wcnf") == 0) { + g_input_kind = IN_WCNF; + } + else if (strcmp(opt_name, "bpo") == 0) { + g_input_kind = IN_OPB; + } else if (strcmp(opt_name, "log") == 0) { g_input_kind = IN_Z3_LOG; } @@ -306,6 +316,12 @@ int main(int argc, char ** argv) { else if (strcmp(ext, "dimacs") == 0 || strcmp(ext, "cnf") == 0) { g_input_kind = IN_DIMACS; } + else if (strcmp(ext, "wcnf") == 0) { + g_input_kind = IN_WCNF; + } + else if (strcmp(ext, "opb") == 0) { + g_input_kind = IN_OPB; + } else if (strcmp(ext, "log") == 0) { g_input_kind = IN_Z3_LOG; } @@ -316,7 +332,7 @@ int main(int argc, char ** argv) { g_input_kind = IN_SMTLIB; } } - } + } switch (g_input_kind) { case IN_SMTLIB: return_value = read_smtlib_file(g_input_file); @@ -328,6 +344,12 @@ int main(int argc, char ** argv) { case IN_DIMACS: return_value = read_dimacs(g_input_file); break; + case IN_WCNF: + return_value = parse_opt(g_input_file, true); + break; + case IN_OPB: + return_value = parse_opt(g_input_file, false); + break; case IN_DATALOG: read_datalog(g_input_file); break; @@ -337,7 +359,7 @@ int main(int argc, char ** argv) { default: UNREACHABLE(); } - memory::finalize(); + memory::finalize(); #ifdef _WINDOWS _CrtDumpMemoryLeaks(); #endif diff --git a/src/shell/opt_frontend.cpp b/src/shell/opt_frontend.cpp new file mode 100644 index 000000000..fa8da4e12 --- /dev/null +++ b/src/shell/opt_frontend.cpp @@ -0,0 +1,384 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include +#include +#include +#include"opt_context.h" +#include"ast_util.h" +#include"arith_decl_plugin.h" +#include"gparams.h" +#include"timeout.h" +#include"reg_decl_plugins.h" + +extern bool g_display_statistics; +static bool g_first_interrupt = true; +static opt::context* g_opt = 0; +static double g_start_time = 0; +static unsigned_vector g_handles; + +class stream_buffer { + std::istream & m_stream; + int m_val; + unsigned m_line; +public: + stream_buffer(std::istream & s): + m_stream(s), + m_line(0) { + m_val = m_stream.get(); + } + int operator *() const { return m_val;} + void operator ++() { m_val = m_stream.get(); } + int ch() const { return m_val; } + void next() { m_val = m_stream.get(); } + bool eof() const { return ch() == EOF; } + unsigned line() const { return m_line; } + void skip_whitespace() { + while ((ch() >= 9 && ch() <= 13) || ch() == 32) { + if (ch() == 10) ++m_line; + next(); + } + } + void skip_space() { + while (ch() != 10 && ((ch() >= 9 && ch() <= 13) || ch() == 32)) { + next(); + } + } + void skip_line() { + while(true) { + if (eof()) { + return; + } + if (ch() == '\n') { + ++m_line; + next(); + return; + } + next(); + } + } + + bool parse_token(char const* token) { + skip_whitespace(); + char const* t = token; + while (ch() == *t) { + next(); + ++t; + } + return 0 == *t; + } + + int parse_int() { + int val = 0; + bool neg = false; + skip_whitespace(); + + if (ch() == '-') { + neg = true; + next(); + } + else if (ch() == '+') { + next(); + } + if (ch() < '0' || ch() > '9') { + std::cerr << "(error line " << line() << " \"unexpected char: " << ((char)ch()) << "\" )\n"; + exit(3); + } + while (ch() >= '0' && ch() <= '9') { + val = val*10 + (ch() - '0'); + next(); + } + return neg ? -val : val; + } + + unsigned parse_unsigned() { + skip_space(); + if (ch() == '\n') { + return UINT_MAX; + } + unsigned val = 0; + while (ch() >= '0' && ch() <= '9') { + val = val*10 + (ch() - '0'); + next(); + } + return val; + } +}; + +class wcnf { + opt::context& opt; + ast_manager& m; + stream_buffer& in; + + app_ref read_clause(unsigned& weight) { + int parsed_lit; + int var; + weight = in.parse_unsigned(); + app_ref result(m), p(m); + expr_ref_vector ors(m); + while (true) { + parsed_lit = in.parse_int(); + if (parsed_lit == 0) + break; + var = abs(parsed_lit); + p = m.mk_const(symbol(var), m.mk_bool_sort()); + if (parsed_lit < 0) p = m.mk_not(p); + ors.push_back(p); + } + result = to_app(mk_or(m, ors.size(), ors.c_ptr())); + return result; + } + + void parse_spec(unsigned& num_vars, unsigned& num_clauses, unsigned& max_weight) { + in.parse_token("wcnf"); + num_vars = in.parse_unsigned(); + num_clauses = in.parse_unsigned(); + max_weight = in.parse_unsigned(); + } + +public: + + wcnf(opt::context& opt, stream_buffer& in): opt(opt), m(opt.get_manager()), in(in) { + opt.set_clausal(true); + } + + void parse() { + unsigned num_vars = 0, num_clauses = 0, max_weight = 0; + while (true) { + in.skip_whitespace(); + if (in.eof()) { + break; + } + else if (*in == 'c') { + in.skip_line(); + } + else if (*in == 'p') { + ++in; + parse_spec(num_vars, num_clauses, max_weight); + } + else { + unsigned weight = 0; + app_ref cls = read_clause(weight); + if (weight >= max_weight) { + opt.add_hard_constraint(cls); + } + else { + unsigned id = opt.add_soft_constraint(cls, rational(weight), symbol::null); + if (g_handles.empty()) { + g_handles.push_back(id); + } + } + } + } + } +}; + + +class opb { + opt::context& opt; + ast_manager& m; + stream_buffer& in; + arith_util arith; + + app_ref parse_id() { + bool negated = in.parse_token("~"); + if (!in.parse_token("x")) { + std::cerr << "(error line " << in.line() << " \"unexpected char: " << ((char)in.ch()) << "\")\n"; + exit(3); + } + app_ref p(m); + int id = in.parse_int(); + p = m.mk_const(symbol(id), m.mk_bool_sort()); + if (negated) p = m.mk_not(p); + in.skip_whitespace(); + return p; + } + + app_ref parse_ids() { + app_ref result = parse_id(); + while (*in == '~' || *in == 'x') { + result = m.mk_and(result, parse_id()); + } + return result; + } + + rational parse_coeff_r() { + in.skip_whitespace(); + svector num; + bool pos = true; + if (*in == '-') pos = false, ++in; + if (*in == '+') ++in; + if (!pos) num.push_back('-'); + in.skip_whitespace(); + while ('0' <= *in && *in <='9') num.push_back(*in), ++in; + num.push_back(0); + return rational(num.c_ptr()); + } + + app_ref parse_coeff() { + return app_ref(arith.mk_numeral(parse_coeff_r(), true), m); + } + + app_ref parse_term() { + app_ref c = parse_coeff(); + app_ref e = parse_ids(); + return app_ref(m.mk_ite(e, c, arith.mk_numeral(rational(0), true)), m); + } + + void parse_objective() { + app_ref t = parse_term(); + while (!in.parse_token(";") && !in.eof()) { + t = arith.mk_add(t, parse_term()); + } + g_handles.push_back(opt.add_objective(t, false)); + } + + void parse_constraint() { + app_ref t = parse_term(); + while (!in.eof()) { + if (in.parse_token(">=")) { + t = arith.mk_ge(t, parse_coeff()); + in.parse_token(";"); + break; + } + if (in.parse_token("=")) { + t = m.mk_eq(t, parse_coeff()); + in.parse_token(";"); + break; + } + t = arith.mk_add(t, parse_term()); + } + opt.add_hard_constraint(t); + } +public: + opb(opt::context& opt, stream_buffer& in): + opt(opt), m(opt.get_manager()), + in(in), arith(m) {} + + void parse() { + while (true) { + in.skip_whitespace(); + if (in.eof()) { + break; + } + else if (*in == '*') { + in.skip_line(); + } + else if (in.parse_token("min:")) { + parse_objective(); + } + else { + parse_constraint(); + } + } + } +}; + + +static void display_results() { + if (g_opt) { + for (unsigned i = 0; i < g_handles.size(); ++i) { + expr_ref lo = g_opt->get_lower(g_handles[i]); + expr_ref hi = g_opt->get_upper(g_handles[i]); + if (lo == hi) { + std::cout << " " << lo << "\n"; + } + else { + std::cout << " [" << lo << ":" << hi << "]\n"; + } + } + } +} + +static void display_statistics() { + if (g_display_statistics && g_opt) { + ::statistics stats; + g_opt->collect_statistics(stats); + stats.display(std::cout); + double end_time = static_cast(clock()); + std::cout << "time: " << (end_time - g_start_time)/CLOCKS_PER_SEC << " secs\n"; + } + + display_results(); +} + +static void on_ctrl_c(int) { + if (g_opt && g_first_interrupt) { + g_opt->set_cancel(true); + g_first_interrupt = false; + } + else { + signal (SIGINT, SIG_DFL); + #pragma omp critical (g_display_stats) + { + display_statistics(); + } + raise(SIGINT); + } +} + +static void on_timeout() { + #pragma omp critical (g_display_stats) + { + display_statistics(); + exit(0); + } +} + +static unsigned parse_opt(std::istream& in, bool is_wcnf) { + ast_manager m; + reg_decl_plugins(m); + opt::context opt(m); + g_opt = &opt; + params_ref p = gparams::get_module("opt"); + opt.updt_params(p); + stream_buffer _in(in); + if (is_wcnf) { + wcnf wcnf(opt, _in); + wcnf.parse(); + } + else { + opb opb(opt, _in); + opb.parse(); + } + try { + lbool r = opt.optimize(); + switch (r) { + case l_true: std::cout << "sat\n"; break; + case l_false: std::cout << "unsat\n"; break; + case l_undef: std::cout << "unknown\n"; break; + } + } + catch (z3_exception & ex) { + std::cerr << ex.msg() << "\n"; + } + #pragma omp critical (g_display_stats) + { + display_statistics(); + register_on_timeout_proc(0); + g_opt = 0; + } + return 0; +} + +unsigned parse_opt(char const* file_name, bool is_wcnf) { + g_first_interrupt = true; + g_start_time = static_cast(clock()); + register_on_timeout_proc(on_timeout); + signal(SIGINT, on_ctrl_c); + if (file_name) { + std::ifstream in(file_name); + if (in.bad() || in.fail()) { + std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; + exit(ERR_OPEN_FILE); + } + return parse_opt(in, is_wcnf); + } + else { + return parse_opt(std::cin, is_wcnf); + } +} + diff --git a/src/shell/opt_frontend.h b/src/shell/opt_frontend.h new file mode 100644 index 000000000..65ec606a5 --- /dev/null +++ b/src/shell/opt_frontend.h @@ -0,0 +1,20 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + opt_frontend.h + +Author: + + Nikolaj Bjorner (nbjorner) 2014-10-10. + +--*/ +#ifndef OPT_FRONTEND_H_ +#define OPT_FRONTEND_H_ + +unsigned parse_opt(char const* file_name, bool is_wcnf); + +#endif /* OPT_FRONTEND_H_ */ + + diff --git a/src/shell/options.h b/src/shell/options.h index 951062ff0..ee1de00fb 100644 --- a/src/shell/options.h +++ b/src/shell/options.h @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + /** \page cmdline Command line options diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index 3197f8cff..eb3b3587d 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -26,6 +26,7 @@ Revision History: #include"smt2parser.h" #include"dl_cmds.h" #include"dbg_cmds.h" +#include"opt_cmds.h" #include"polynomial_cmds.h" #include"subpaving_cmds.h" #include"smt_strategic_solver.h" @@ -112,6 +113,7 @@ unsigned read_smtlib2_commands(char const * file_name) { install_dbg_cmds(ctx); install_polynomial_cmds(ctx); install_subpaving_cmds(ctx); + install_opt_cmds(ctx); g_cmd_context = &ctx; signal(SIGINT, on_ctrl_c); diff --git a/src/shell/smtlib_frontend.h b/src/shell/smtlib_frontend.h index 2c5eed70c..1d9bb55de 100644 --- a/src/shell/smtlib_frontend.h +++ b/src/shell/smtlib_frontend.h @@ -16,12 +16,12 @@ Author: Revision History: --*/ -#ifndef _SMTLIB_FRONTEND_H_ -#define _SMTLIB_FRONTEND_H_ +#ifndef SMTLIB_FRONTEND_H_ +#define SMTLIB_FRONTEND_H_ unsigned read_smtlib_file(char const * benchmark_file); unsigned read_smtlib2_commands(char const * command_file); -#endif /* _SMTLIB_FRONTEND_H_ */ +#endif /* SMTLIB_FRONTEND_H_ */ diff --git a/src/shell/z3_log_frontend.h b/src/shell/z3_log_frontend.h index bd2c5258a..8eba9b774 100644 --- a/src/shell/z3_log_frontend.h +++ b/src/shell/z3_log_frontend.h @@ -17,11 +17,11 @@ Author: Revision History: --*/ -#ifndef _Z3_LOG_FRONTEND_H_ -#define _Z3_LOG_FRONTEND_H_ +#ifndef Z3_LOG_FRONTEND_H_ +#define Z3_LOG_FRONTEND_H_ void replay_z3_log(char const * benchmark_file); -#endif /* _Z3_FRONTEND_H_ */ +#endif /* Z3_FRONTEND_H_ */ diff --git a/src/smt/arith_eq_adapter.cpp b/src/smt/arith_eq_adapter.cpp index 53227d0cc..74e3d573a 100644 --- a/src/smt/arith_eq_adapter.cpp +++ b/src/smt/arith_eq_adapter.cpp @@ -287,12 +287,13 @@ namespace smt { } void arith_eq_adapter::restart_eh() { + context & ctx = get_context(); TRACE("arith_eq_adapter", tout << "restart\n";); svector tmp(m_restart_pairs); svector::iterator it = tmp.begin(); svector::iterator end = tmp.end(); m_restart_pairs.reset(); - for (; it != end; ++it) { + for (; it != end && !ctx.inconsistent(); ++it) { TRACE("arith_eq_adapter", tout << "creating arith_eq_adapter axioms at the base level #" << it->first->get_owner_id() << " #" << it->second->get_owner_id() << "\n";); mk_axioms(it->first, it->second); diff --git a/src/smt/arith_eq_adapter.h b/src/smt/arith_eq_adapter.h index 71fb79136..ffbd15c26 100644 --- a/src/smt/arith_eq_adapter.h +++ b/src/smt/arith_eq_adapter.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ARITH_EQ_ADAPTER_H_ -#define _ARITH_EQ_ADAPTER_H_ +#ifndef ARITH_EQ_ADAPTER_H_ +#define ARITH_EQ_ADAPTER_H_ #include"smt_theory.h" #include"obj_pair_hashtable.h" @@ -93,5 +93,5 @@ namespace smt { }; }; -#endif /* _ARITH_EQ_ADAPTER_H_ */ +#endif /* ARITH_EQ_ADAPTER_H_ */ diff --git a/src/smt/arith_eq_solver.h b/src/smt/arith_eq_solver.h index e17ae2d30..8b7e99fcc 100644 --- a/src/smt/arith_eq_solver.h +++ b/src/smt/arith_eq_solver.h @@ -14,8 +14,8 @@ Author: Nikolaj Bjorner (nbjorner) 2012-02-25 --*/ -#ifndef _ARITH_EQ_SOLVER_H_ -#define _ARITH_EQ_SOLVER_H_ +#ifndef ARITH_EQ_SOLVER_H_ +#define ARITH_EQ_SOLVER_H_ #include"arith_decl_plugin.h" #include"arith_rewriter.h" @@ -105,4 +105,4 @@ public: }; -#endif /* _ARITH_EQ_SOLVER_H_ */ +#endif /* ARITH_EQ_SOLVER_H_ */ diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index c02c6760a..bd5f53922 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -252,6 +252,7 @@ void asserted_formulas::reduce() { TRACE("before_reduce", display(tout);); CASSERT("well_sorted", check_well_sorted()); + #define INVOKE(COND, FUNC) if (COND) { FUNC; IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); } TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); TRACE("reduce_step", display(tout << #FUNC << " ");); CASSERT("well_sorted",check_well_sorted()); if (inconsistent() || canceled()) { TRACE("after_reduce", display(tout);); TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); return; } set_eliminate_and(false); // do not eliminate and before nnf. diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h index e888a974e..33781abf6 100644 --- a/src/smt/asserted_formulas.h +++ b/src/smt/asserted_formulas.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ASSERTED_FORMULAS_H_ -#define _ASSERTED_FORMULAS_H_ +#ifndef ASSERTED_FORMULAS_H_ +#define ASSERTED_FORMULAS_H_ #include"smt_params.h" #include"simplifier.h" @@ -148,5 +148,5 @@ public: }; -#endif /* _ASSERTED_FORMULAS_H_ */ +#endif /* ASSERTED_FORMULAS_H_ */ diff --git a/src/smt/cached_var_subst.h b/src/smt/cached_var_subst.h index 65dbf22d2..f9a08a810 100644 --- a/src/smt/cached_var_subst.h +++ b/src/smt/cached_var_subst.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _CACHED_VAR_SUBST_H_ -#define _CACHED_VAR_SUBST_H_ +#ifndef CACHED_VAR_SUBST_H_ +#define CACHED_VAR_SUBST_H_ #include"var_subst.h" #include"map.h" @@ -49,5 +49,5 @@ public: void reset(); }; -#endif /* _CACHED_VAR_SUBST_H_ */ +#endif /* CACHED_VAR_SUBST_H_ */ diff --git a/src/smt/cost_evaluator.h b/src/smt/cost_evaluator.h index c32d7a924..e6f7fa6e0 100644 --- a/src/smt/cost_evaluator.h +++ b/src/smt/cost_evaluator.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _COST_EVALUATOR_H_ -#define _COST_EVALUATOR_H_ +#ifndef COST_EVALUATOR_H_ +#define COST_EVALUATOR_H_ #include"ast.h" #include"arith_decl_plugin.h" @@ -39,5 +39,5 @@ public: float operator()(expr * f, unsigned num_args, float const * args); }; -#endif /* _COST_EVALUATOR_H_ */ +#endif /* COST_EVALUATOR_H_ */ diff --git a/src/smt/database.h b/src/smt/database.h index 69843f434..3edb87f1f 100644 --- a/src/smt/database.h +++ b/src/smt/database.h @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + static char const g_pattern_database[] = "(benchmark patterns \n" " :status unknown \n" diff --git a/src/smt/diff_logic.h b/src/smt/diff_logic.h index 2717e4a92..daaed13ac 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DIFF_LOGIC_H_ -#define _DIFF_LOGIC_H_ +#ifndef DIFF_LOGIC_H_ +#define DIFF_LOGIC_H_ #include"vector.h" #include"heap.h" @@ -25,6 +25,7 @@ Revision History: #include"trace.h" #include"warning.h" #include"uint_set.h" +#include typedef int dl_var; @@ -281,6 +282,8 @@ public: unsigned get_num_edges() const { return m_edges.size(); } + unsigned get_num_nodes() const { return m_out_edges.size(); } + dl_var get_source(edge_id id) const { return m_edges[id].get_source(); } dl_var get_target(edge_id id) const { return m_edges[id].get_target(); } @@ -931,6 +934,120 @@ public: return found; } + // Return true if there is an edge source --> target (also counting disabled edges). + // If there is such edge, return its edge_id in parameter id. + bool get_edge_id(dl_var source, dl_var target, edge_id & id) const { + edge_id_vector const & edges = m_out_edges[source]; + typename edge_id_vector::const_iterator it = edges.begin(); + typename edge_id_vector::const_iterator end = edges.end(); + for (; it != end; ++it) { + id = *it; + edge const & e = m_edges[id]; + if (e.get_target() == target) { + return true; + } + } + return false; + } + + edges const & get_all_edges() const { + return m_edges; + } + + void get_neighbours_undirected(dl_var current, svector & neighbours) { + neighbours.reset(); + edge_id_vector & out_edges = m_out_edges[current]; + typename edge_id_vector::iterator it = out_edges.begin(), end = out_edges.end(); + for (; it != end; ++it) { + edge_id e_id = *it; + edge & e = m_edges[e_id]; + SASSERT(e.get_source() == current); + dl_var neighbour = e.get_target(); + neighbours.push_back(neighbour); + } + edge_id_vector & in_edges = m_in_edges[current]; + typename edge_id_vector::iterator it2 = in_edges.begin(), end2 = in_edges.end(); + for (; it2 != end2; ++it2) { + edge_id e_id = *it2; + edge & e = m_edges[e_id]; + SASSERT(e.get_target() == current); + dl_var neighbour = e.get_source(); + neighbours.push_back(neighbour); + } + } + + void dfs_undirected(dl_var start, svector & threads) { + threads.reset(); + threads.resize(get_num_nodes()); + uint_set discovered, explored; + svector nodes; + discovered.insert(start); + nodes.push_back(start); + dl_var prev = start; + while(!nodes.empty()) { + dl_var current = nodes.back(); + SASSERT(discovered.contains(current) && !explored.contains(current)); + svector neighbours; + get_neighbours_undirected(current, neighbours); + SASSERT(!neighbours.empty()); + bool found = false; + for (unsigned i = 0; i < neighbours.size(); ++i) { + dl_var next = neighbours[i]; + DEBUG_CODE( + edge_id id; + SASSERT(get_edge_id(current, next, id) || get_edge_id(next, current, id));); + if (!discovered.contains(next) && !explored.contains(next)) { + TRACE("diff_logic", tout << "thread[" << prev << "] --> " << next << std::endl;); + threads[prev] = next; + prev = next; + discovered.insert(next); + nodes.push_back(next); + found = true; + break; + } + } + SASSERT(!nodes.empty()); + if (!found) { + explored.insert(current); + nodes.pop_back(); + } + } + threads[prev] = start; + } + + void bfs_undirected(dl_var start, svector & parents, svector & depths) { + parents.reset(); + parents.resize(get_num_nodes()); + parents[start] = -1; + depths.reset(); + depths.resize(get_num_nodes()); + uint_set visited; + std::deque nodes; + visited.insert(start); + nodes.push_front(start); + while(!nodes.empty()) { + dl_var current = nodes.back(); + nodes.pop_back(); + SASSERT(visited.contains(current)); + svector neighbours; + get_neighbours_undirected(current, neighbours); + SASSERT(!neighbours.empty()); + for (unsigned i = 0; i < neighbours.size(); ++i) { + dl_var next = neighbours[i]; + DEBUG_CODE( + edge_id id; + SASSERT(get_edge_id(current, next, id) || get_edge_id(next, current, id));); + if (!visited.contains(next)) { + TRACE("diff_logic", tout << "parents[" << next << "] --> " << current << std::endl;); + parents[next] = current; + depths[next] = depths[current] + 1; + visited.insert(next); + nodes.push_front(next); + } + } + } + } + template void enumerate_edges(dl_var source, dl_var target, Functor& f) { edge_id_vector & edges = m_out_edges[source]; @@ -1076,6 +1193,10 @@ public: return m_assignment[v]; } + void set_assignment(dl_var v, numeral const & n) { + m_assignment[v] = n; + } + unsigned get_timestamp() const { return m_timestamp; } @@ -1711,5 +1832,5 @@ public: } }; -#endif /* _DIFF_LOGIC_H_ */ +#endif /* DIFF_LOGIC_H_ */ diff --git a/src/smt/dyn_ack.h b/src/smt/dyn_ack.h index b0fe7a6b3..0c390f0ba 100644 --- a/src/smt/dyn_ack.h +++ b/src/smt/dyn_ack.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _DYN_ACK_H_ -#define _DYN_ACK_H_ +#ifndef DYN_ACK_H_ +#define DYN_ACK_H_ #include"ast.h" #include"dyn_ack_params.h" @@ -133,5 +133,5 @@ namespace smt { }; -#endif /* _DYN_ACK_H_ */ +#endif /* DYN_ACK_H_ */ diff --git a/src/smt/elim_term_ite.cpp b/src/smt/elim_term_ite.cpp index 2e1671f43..74b804c21 100644 --- a/src/smt/elim_term_ite.cpp +++ b/src/smt/elim_term_ite.cpp @@ -33,16 +33,16 @@ void elim_term_ite::operator()(expr * n, proof * pr2; get_cached(n, r2, pr2); r = r2; - switch (m_manager.proof_mode()) { + switch (m.proof_mode()) { case PGM_DISABLED: - pr = m_manager.mk_undef_proof(); + pr = m.mk_undef_proof(); break; case PGM_COARSE: remove_duplicates(m_coarse_proofs); - pr = n == r2 ? m_manager.mk_oeq_reflexivity(n) : m_manager.mk_apply_defs(n, r, m_coarse_proofs.size(), m_coarse_proofs.c_ptr()); + pr = n == r2 ? m.mk_oeq_reflexivity(n) : m.mk_apply_defs(n, r, m_coarse_proofs.size(), m_coarse_proofs.c_ptr()); break; case PGM_FINE: - pr = pr2 == 0 ? m_manager.mk_oeq_reflexivity(n) : pr2; + pr = pr2 == 0 ? m.mk_oeq_reflexivity(n) : pr2; break; } m_coarse_proofs.reset(); @@ -107,36 +107,36 @@ void elim_term_ite::reduce1_app(app * n) { m_args.reset(); func_decl * decl = n->get_decl(); - proof_ref p1(m_manager); + proof_ref p1(m); get_args(n, m_args, p1); - if (!m_manager.fine_grain_proofs()) + if (!m.fine_grain_proofs()) p1 = 0; - expr_ref r(m_manager); - r = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr()); - if (m_manager.is_term_ite(r)) { - expr_ref new_def(m_manager); - proof_ref new_def_pr(m_manager); - app_ref new_r(m_manager); - proof_ref new_pr(m_manager); + expr_ref r(m); + r = m.mk_app(decl, m_args.size(), m_args.c_ptr()); + if (m.is_term_ite(r)) { + expr_ref new_def(m); + proof_ref new_def_pr(m); + app_ref new_r(m); + proof_ref new_pr(m); if (m_defined_names.mk_name(r, new_def, new_def_pr, new_r, new_pr)) { - CTRACE("elim_term_ite_bug", new_def.get() == 0, tout << mk_ismt2_pp(r, m_manager) << "\n";); + CTRACE("elim_term_ite_bug", new_def.get() == 0, tout << mk_ismt2_pp(r, m) << "\n";); SASSERT(new_def.get() != 0); m_new_defs->push_back(new_def); - if (m_manager.fine_grain_proofs()) { + if (m.fine_grain_proofs()) { m_new_def_proofs->push_back(new_def_pr); - new_pr = m_manager.mk_transitivity(p1, new_pr); + new_pr = m.mk_transitivity(p1, new_pr); } else { // [Leo] This looks fishy... why do we add 0 into m_coarse_proofs when fine_grain_proofs are disabled? new_pr = 0; - if (m_manager.proofs_enabled()) + if (m.proofs_enabled()) m_coarse_proofs.push_back(new_pr); } } else { SASSERT(new_def.get() == 0); - if (!m_manager.fine_grain_proofs()) + if (!m.fine_grain_proofs()) new_pr = 0; } cache_result(n, new_r, new_pr); @@ -151,8 +151,8 @@ void elim_term_ite::reduce1_quantifier(quantifier * q) { proof * new_body_pr; get_cached(q->get_expr(), new_body, new_body_pr); - quantifier * new_q = m_manager.update_quantifier(q, new_body); - proof * p = q == new_q ? 0 : m_manager.mk_oeq_quant_intro(q, new_q, new_body_pr); + quantifier * new_q = m.update_quantifier(q, new_body); + proof * p = q == new_q ? 0 : m.mk_oeq_quant_intro(q, new_q, new_body_pr); cache_result(q, new_q, p); } diff --git a/src/smt/elim_term_ite.h b/src/smt/elim_term_ite.h index 5c15af9e5..fc682b39a 100644 --- a/src/smt/elim_term_ite.h +++ b/src/smt/elim_term_ite.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _ELIM_TERM_ITE_H_ -#define _ELIM_TERM_ITE_H_ +#ifndef ELIM_TERM_ITE_H_ +#define ELIM_TERM_ITE_H_ #include"simplifier.h" #include"defined_names.h" @@ -46,5 +46,5 @@ public: ); }; -#endif /* _ELIM_TERM_ITE_H_ */ +#endif /* ELIM_TERM_ITE_H_ */ diff --git a/src/smt/expr_context_simplifier.h b/src/smt/expr_context_simplifier.h index a1d01b78c..07183ad12 100644 --- a/src/smt/expr_context_simplifier.h +++ b/src/smt/expr_context_simplifier.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _EXPR_CONTEXT_SIMPLIFIER_H_ -#define _EXPR_CONTEXT_SIMPLIFIER_H_ +#ifndef EXPR_CONTEXT_SIMPLIFIER_H_ +#define EXPR_CONTEXT_SIMPLIFIER_H_ #include "ast.h" #include "obj_hashtable.h" @@ -80,5 +80,5 @@ public: void set_cancel(bool f) { m_solver.set_cancel(f); } }; -#endif /* _EXPR_CONTEXT_SIMPLIFIER_H__ */ +#endif /* EXPR_CONTEXT_SIMPLIFIER_H_ */ diff --git a/src/smt/fingerprints.h b/src/smt/fingerprints.h index e9045990d..80905f8be 100644 --- a/src/smt/fingerprints.h +++ b/src/smt/fingerprints.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _FINGERPRINTS_H_ -#define _FINGERPRINTS_H_ +#ifndef FINGERPRINTS_H_ +#define FINGERPRINTS_H_ #include"smt_enode.h" @@ -81,5 +81,5 @@ namespace smt { }; }; -#endif /* _FINGERPRINTS_H_ */ +#endif /* FINGERPRINTS_H_ */ diff --git a/src/smt/mam.h b/src/smt/mam.h index c9f813150..dd56d7984 100644 --- a/src/smt/mam.h +++ b/src/smt/mam.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _MAM_H_ -#define _MAM_H_ +#ifndef MAM_H_ +#define MAM_H_ #include"ast.h" #include"smt_types.h" @@ -69,4 +69,4 @@ namespace smt { mam * mk_mam(context & ctx); }; -#endif /* _MAM_H_ */ +#endif /* MAM_H_ */ diff --git a/src/smt/old_interval.cpp b/src/smt/old_interval.cpp index 9dd8e0955..616b74ed6 100644 --- a/src/smt/old_interval.cpp +++ b/src/smt/old_interval.cpp @@ -72,7 +72,11 @@ ext_numeral & ext_numeral::operator-=(ext_numeral const & other) { } ext_numeral & ext_numeral::operator*=(ext_numeral const & other) { - if (is_zero() || other.is_zero()) { + if (is_zero()) { + m_kind = FINITE; + return *this; + } + if (other.is_zero()) { m_kind = FINITE; m_value.reset(); return *this; diff --git a/src/smt/old_interval.h b/src/smt/old_interval.h index ff2ae4b21..b2f1d8fbd 100644 --- a/src/smt/old_interval.h +++ b/src/smt/old_interval.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _OLD_INTERVAL_H_ -#define _OLD_INTERVAL_H_ +#ifndef OLD_INTERVAL_H_ +#define OLD_INTERVAL_H_ #include"rational.h" #include"dependency.h" @@ -134,5 +134,5 @@ inline std::ostream & operator<<(std::ostream & out, std::pair