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/RELEASE_NOTES b/RELEASE_NOTES index afa613f57..2c7e2e0fb 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,6 +1,6 @@ RELEASE NOTES -Version 4.4 +Version 4.4.0 ============= - New feature: Support for the theory of floating-point numbers. This comes in the form of logics (QF_FP and QF_FPBV), tactics (qffp and qffpbv), as well as a theory plugin that allows theory combinations. Z3 supports the official SMT theory definition of FP (see http://smtlib.cs.uiowa.edu/theories/FloatingPoint.smt2) in SMT2 files, as well as all APIs. 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..b32965c07 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); 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/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..25a8e2d26 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3.Tests/ServiceTests.cs @@ -0,0 +1,86 @@ +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..c2cd0c270 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3.Tests/SolverTests.cs @@ -0,0 +1,132 @@ +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..99d6fe17a --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/AbortWorker.cs @@ -0,0 +1,92 @@ +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..71c8647a1 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Utils.cs @@ -0,0 +1,124 @@ +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..e1403f698 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseDirective.cs @@ -0,0 +1,101 @@ +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..6585181ec --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseParams.cs @@ -0,0 +1,103 @@ +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..54e3893f0 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs @@ -0,0 +1,381 @@ +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..69f9ff6c1 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPDirective.cs @@ -0,0 +1,9 @@ +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..38bd9040a --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPParams.cs @@ -0,0 +1,19 @@ +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..b31f6de97 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3MILPSolver.cs @@ -0,0 +1,230 @@ +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..12dcb6e84 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermDirective.cs @@ -0,0 +1,9 @@ +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..48a90afe1 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermParams.cs @@ -0,0 +1,17 @@ +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..3317b9a4d --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3TermSolver.cs @@ -0,0 +1,382 @@ +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..758c65c78 --- /dev/null +++ b/examples/msf/Validator/Program.cs @@ -0,0 +1,194 @@ +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/python/hamiltonian/hamiltonian.py b/examples/python/hamiltonian/hamiltonian.py new file mode 100644 index 000000000..fc158b939 --- /dev/null +++ b/examples/python/hamiltonian/hamiltonian.py @@ -0,0 +1,88 @@ +############################################ +# Copyright (c) 2012 Ganesh Gopalakrishnan ganesh@cs.utah.edu +# +# Check if the given graph has a Hamiltonian cycle. +# +# Author: Ganesh Gopalakrishnan ganesh@cs.utah.edu +############################################ +from z3 import * + +def gencon(gr): + """ + Input a graph as an adjacency list, e.g. {0:[1,2], 1:[2], 2:[1,0]}. + Produces solver to check if the given graph has + a Hamiltonian cycle. Query the solver using s.check() and if sat, + then s.model() spells out the cycle. Two example graphs from + http://en.wikipedia.org/wiki/Hamiltonian_path are tested. + + ======================================================= + + Explanation: + + Generate a list of Int vars. Constrain the first Int var ("Node 0") to be 0. + Pick a node i, and attempt to number all nodes reachable from i to have a + number one higher (mod L) than assigned to node i (use an Or constraint). + + ======================================================= + """ + L = len(gr) + cv = [Int('cv%s'%i) for i in range(L)] + s = Solver() + s.add(cv[0]==0) + for i in range(L): + s.add(Or([cv[j]==(cv[i]+1)%L for j in gr[i]])) + return s + +def examples(): + # Example Graphs: The Dodecahedral graph from http://en.wikipedia.org/wiki/Hamiltonian_path + grdodec = { 0: [1, 4, 5], + 1: [0, 7, 2], + 2: [1, 9, 3], + 3: [2, 11, 4], + 4: [3, 13, 0], + 5: [0, 14, 6], + 6: [5, 16, 7], + 7: [6, 8, 1], + 8: [7, 17, 9], + 9: [8, 10, 2], + 10: [9, 18, 11], + 11: [10, 3, 12], + 12: [11, 19, 13], + 13: [12, 14, 4], + 14: [13, 15, 5], + 15: [14, 16, 19], + 16: [6, 17, 15], + 17: [16, 8, 18], + 18: [10, 19, 17], + 19: [18, 12, 15] } + import pprint + pp = pprint.PrettyPrinter(indent=4) + pp.pprint(grdodec) + + sdodec=gencon(grdodec) + print(sdodec.check()) + print(sdodec.model()) + # ======================================================= + # See http://en.wikipedia.org/wiki/Hamiltonian_path for the Herschel graph + # being the smallest possible polyhdral graph that does not have a Hamiltonian + # cycle. + # + grherschel = { 0: [1, 9, 10, 7], + 1: [0, 8, 2], + 2: [1, 9, 3], + 3: [2, 8, 4], + 4: [3, 9, 10, 5], + 5: [4, 8, 6], + 6: [5, 10, 7], + 7: [6, 8, 0], + 8: [1, 3, 5, 7], + 9: [2, 0, 4], + 10: [6, 4, 0] } + pp.pprint(grherschel) + sherschel=gencon(grherschel) + print(sherschel.check()) + # ======================================================= + +if __name__ == "__main__": + examples() + 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..5ac185de0 --- /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'): + 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..b9641c869 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,40 @@ 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('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('smtparser', ['portfolio'], 'parsers/smt') + add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic'], 'opt') # add_dll('foci2', ['util'], 'interp/foci2stub', # dll_name='foci2', # export_files=['foci2stub.cpp']) # add_lib('interp', ['solver','foci2']) 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 547379aec..820396c0d 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -85,6 +85,10 @@ VS_PAR=False VS_PAR_NUM=8 GPROF=False GIT_HASH=False +OPTIMIZE=False + +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') @@ -227,10 +231,37 @@ def test_openmp(cc): t.add('#include\nint main() { return omp_in_parallel() ? 1 : 0; }\n') t.commit() if IS_WINDOWS: - return exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '/openmp']) == 0 + r = exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '/openmp']) == 0 + try: + rmf('tstomp.obj') + rmf('tstomp.exe') + except: + pass + return r 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: @@ -357,10 +388,14 @@ def check_ml(): r = exec_cmd([OCAMLOPT, '-o', 'a.out', 'hello.ml']) if r != 0: raise MKException('Failed testing ocamlopt compiler. Set environment variable OCAMLOPT with the path to the Ocaml native compiler. Note that ocamlopt may require flexlink to be in your path.') - rmf('hello.cmi') - rmf('hello.cmo') - rmf('hello.cmx') - rmf('a.out') + try: + rmf('hello.cmi') + rmf('hello.cmo') + rmf('hello.cmx') + rmf('a.out') + rmf('hello.o') + except: + pass find_ml_lib() find_ocaml_find() @@ -517,6 +552,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.") @@ -543,13 +580,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, OPTIMIZE 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']) except: print("ERROR: Invalid command line option") display_help(1) @@ -584,6 +621,8 @@ def parse_options(): DOTNET_ENABLED = False elif opt in ('--staticlib'): STATIC_LIB = True + elif opt in ('--optimize'): + 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') @@ -1712,7 +1751,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' @@ -1731,22 +1769,25 @@ 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 + if OPTIMIZE: + config.write('AR_FLAGS=/nologo /LTCG\n') config.write( 'LINK_FLAGS=/nologo /MD\n' 'SLINK_FLAGS=/nologo /LD\n') @@ -1777,7 +1818,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() @@ -1806,9 +1847,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: @@ -1849,7 +1892,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 @@ -1861,7 +1905,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) @@ -1892,6 +1935,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 33d7a3eab..fb42750e3 100644 --- a/scripts/mk_win_dist.py +++ b/scripts/mk_win_dist.py @@ -47,16 +47,16 @@ def set_build_dir(path): mk_dir(BUILD_X64_DIR) def display_help(): - print "mk_win_dist.py: Z3 Windows distribution generator\n" - print "This script generates the zip files containing executables, dlls, header files for Windows." - print "It must be executed from the Z3 root directory." - print "\nOptions:" - print " -h, --help display this message." - print " -s, --silent do not print verbose messages." - print " -b , --build= subdirectory where x86 and x64 Z3 versions will be built (default: build-dist)." - print " -f, --force force script to regenerate Makefiles." - print " --nojava do not include Java bindings in the binary distribution files." - print " --githash include git hash in the Zip file." + print("mk_win_dist.py: Z3 Windows distribution generator\n") + print("This script generates the zip files containing executables, dlls, header files for Windows.") + print("It must be executed from the Z3 root directory.") + print("\nOptions:") + print(" -h, --help display this message.") + print(" -s, --silent do not print verbose messages.") + print(" -b , --build= subdirectory where x86 and x64 Z3 versions will be built (default: build-dist).") + print(" -f, --force force script to regenerate Makefiles.") + print(" --nojava do not include Java bindings in the binary distribution files.") + print(" --githash include git hash in the Zip file.") exit(0) # Parse configuration option for mk_make script @@ -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/src/api/api_ast.cpp b/src/api/api_ast.cpp index 20ef9ba7f..83ad607ba 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" @@ -1081,7 +1082,6 @@ extern "C" { case OP_BSREM_I: case OP_BUREM_I: case OP_BSMOD_I: - return Z3_OP_UNINTERPRETED; default: UNREACHABLE(); @@ -1090,9 +1090,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 +1187,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_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..c857be48d 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(); diff --git a/src/api/api_context.h b/src/api/api_context.h index f02dc6f16..b5a15d657 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.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; @@ -127,6 +128,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; } diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index ad6b5ad69..2d2b50f3d 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(); @@ -466,13 +466,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_datatype.cpp b/src/api/api_datatype.cpp index a9794c980..d468f6773 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( + __in Z3_context c, __in Z3_func_decl f, __in Z3_ast t, __in 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..e79a04cad 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)); @@ -765,7 +765,30 @@ extern "C" { mpqm.display_decimal(ss, q, sbits); return mk_c(c)->mk_external_string(ss.str()); Z3_CATCH_RETURN(""); + } + Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(__in Z3_context c, __in Z3_ast t, __out __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(__in Z3_context c, __in Z3_ast t) { @@ -794,7 +817,7 @@ extern "C" { Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(__in Z3_context c, __in Z3_ast t, __out __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(); diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 615b798bb..8a607bff1 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -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); } diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 0ae1a9948..5ae36532b 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -65,6 +65,18 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_bool Z3_API Z3_model_has_interp(Z3_context c, Z3_model m, Z3_func_decl a) { + Z3_TRY; + LOG_Z3_model_has_interp(c, m, a); + CHECK_NON_NULL(m, 0); + if (to_model_ref(m)->has_interpretation(to_func_decl(a))) { + return Z3_TRUE; + } else { + return Z3_FALSE; + } + Z3_CATCH_RETURN(Z3_FALSE); + } + Z3_func_interp Z3_API Z3_model_get_func_interp(Z3_context c, Z3_model m, Z3_func_decl f) { Z3_TRY; LOG_Z3_model_get_func_interp(c, m, f); diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp new file mode 100644 index 000000000..d9369092a --- /dev/null +++ b/src/api/api_opt.cpp @@ -0,0 +1,243 @@ +/*++ +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_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_solver.cpp b/src/api/api_solver.cpp index e57050e82..ca15762f5 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -355,6 +355,10 @@ extern "C" { init_solver(c, s); Z3_stats_ref * st = alloc(Z3_stats_ref); to_solver_ref(s)->collect_statistics(st->m_stats); + unsigned long long max_mem = memory::get_max_used_memory(); + unsigned long long mem = memory::get_allocation_size(); + st->m_stats.update("max memory", static_cast(max_mem)/(1024.0*1024.0)); + st->m_stats.update("memory", static_cast(mem)/(1024.0*1024.0)); mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index f43ba83e5..cd47d6900 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -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..a717efbc2 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 @@ -2251,6 +2264,36 @@ namespace Microsoft.Z3 } #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 + #region Numerals #region General Numerals @@ -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..e6e258b9a 100644 --- a/src/api/dotnet/InterpolationContext.cs +++ b/src/api/dotnet/InterpolationContext.cs @@ -47,7 +47,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 +59,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 +68,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 +80,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 +102,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..dc94db78a --- /dev/null +++ b/src/api/dotnet/Optimize.cs @@ -0,0 +1,298 @@ +/*++ +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)); + } + + + /// + /// 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/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..1cab00342 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. **/ 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..e1fccdf4f 100644 --- a/src/api/java/Fixedpoint.java +++ b/src/api/java/Fixedpoint.java @@ -295,14 +295,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 +306,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/Goal.java b/src/api/java/Goal.java index 18aecb557..41d4e27ac 100644 --- a/src/api/java/Goal.java +++ b/src/api/java/Goal.java @@ -221,6 +221,22 @@ public class Goal extends Z3Object return "Z3Exception: " + e.getMessage(); } } + + /** + * Goal to BoolExpr conversion. + * + * Returns a string representation of the Goal. + **/ + public BoolExpr AsBoolExpr() { + int n = size(); + if (n == 0) + return getContext().mkTrue(); + else if (n == 1) + return getFormulas()[0]; + else { + return getContext().mkAnd(getFormulas()); + } + } Goal(Context ctx, long obj) { diff --git a/src/api/java/InterpolationContext.java b/src/api/java/InterpolationContext.java index 897c0e9e9..05746af5d 100644 --- a/src/api/java/InterpolationContext.java +++ b/src/api/java/InterpolationContext.java @@ -73,20 +73,23 @@ public class InterpolationContext extends Context * well documented. * @throws Z3Exception **/ - public Expr[] GetInterpolant(Expr pf, Expr pat, Params p) + public BoolExpr[] GetInterpolant(Expr pf, Expr pat, Params p) { checkContextMatch(pf); checkContextMatch(pat); checkContextMatch(p); ASTVector seq = new ASTVector(this, Native.getInterpolant(nCtx(), pf.getNativeObject(), pat.getNativeObject(), p.getNativeObject())); - int n = seq.size(); - Expr[] res = new Expr[n]; - for (int i = 0; i < n; i++) - res[i] = Expr.create(this, seq.get(i).getNativeObject()); - return res; + return seq.ToBoolExprArray(); } + public class ComputeInterpolantResult + { + public Z3_lbool status = Z3_lbool.Z3_L_UNDEF; + public BoolExpr[] interp = null; + public Model model = null; + }; + /** * Computes an interpolant. * Remarks: For more information on interpolation please refer @@ -94,17 +97,20 @@ public class InterpolationContext extends Context * well documented. * @throws Z3Exception **/ - public Z3_lbool ComputeInterpolant(Expr pat, Params p, ASTVector interp, Model model) + public ComputeInterpolantResult ComputeInterpolant(Expr pat, Params p) { checkContextMatch(pat); checkContextMatch(p); + ComputeInterpolantResult res = new ComputeInterpolantResult(); Native.LongPtr n_i = new Native.LongPtr(); Native.LongPtr n_m = new Native.LongPtr(); - int r = Native.computeInterpolant(nCtx(), pat.getNativeObject(), p.getNativeObject(), n_i, n_m); - interp = new ASTVector(this, n_i.value); - model = new Model(this, n_m.value); - return Z3_lbool.fromInt(r); + res.status = Z3_lbool.fromInt(Native.computeInterpolant(nCtx(), pat.getNativeObject(), p.getNativeObject(), n_i, n_m)); + if (res.status == Z3_lbool.Z3_L_FALSE) + res.interp = (new ASTVector(this, n_i.value)).ToBoolExprArray(); + if (res.status == Z3_lbool.Z3_L_TRUE) + res.model = new Model(this, n_m.value); + return res; } /// @@ -118,16 +124,23 @@ public class InterpolationContext extends Context return Native.interpolationProfile(nCtx()); } + public class CheckInterpolantResult + { + public int return_value = 0; + public String error = null; + } + /// /// Checks the correctness of an interpolant. /// /// Remarks: 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, int[] parents, Expr[] interps, String error, Expr[] theory) + public CheckInterpolantResult CheckInterpolant(Expr[] cnsts, int[] parents, BoolExpr[] interps, String error, Expr[] theory) { + CheckInterpolantResult res = new CheckInterpolantResult(); Native.StringPtr n_err_str = new Native.StringPtr(); - int r = Native.checkInterpolant(nCtx(), + res.return_value = Native.checkInterpolant(nCtx(), cnsts.length, Expr.arrayToNative(cnsts), parents, @@ -135,41 +148,52 @@ public class InterpolationContext extends Context n_err_str, theory.length, Expr.arrayToNative(theory)); - error = n_err_str.value; - return r; + res.error = n_err_str.value; + return res; } + public class ReadInterpolationProblemResult + { + public int return_value = 0; + public Expr[] cnsts; + public int[] parents; + public String error; + public Expr[] theory; + }; + /// /// Reads an interpolation problem from a file. /// /// Remarks: For more information on interpolation please refer /// too the function Z3_read_interpolation_problem in the C/C++ API, which is /// well documented. - public int ReadInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) + public ReadInterpolationProblemResult ReadInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) { + ReadInterpolationProblemResult res = new ReadInterpolationProblemResult(); + Native.IntPtr n_num = new Native.IntPtr(); Native.IntPtr n_num_theory = new Native.IntPtr(); Native.ObjArrayPtr n_cnsts = new Native.ObjArrayPtr(); Native.UIntArrayPtr n_parents = new Native.UIntArrayPtr(); Native.ObjArrayPtr n_theory = new Native.ObjArrayPtr(); Native.StringPtr n_err_str = new Native.StringPtr(); - int r = Native.readInterpolationProblem(nCtx(), n_num, n_cnsts, n_parents, filename, n_err_str, n_num_theory, n_theory); + res.return_value = Native.readInterpolationProblem(nCtx(), n_num, n_cnsts, n_parents, filename, n_err_str, n_num_theory, n_theory); int num = n_num.value; int num_theory = n_num_theory.value; - error = n_err_str.value; - cnsts = new Expr[num]; - parents = new int[num]; + res.error = n_err_str.value; + res.cnsts = new Expr[num]; + res.parents = new int[num]; theory = new Expr[num_theory]; for (int i = 0; i < num; i++) { - cnsts[i] = Expr.create(this, n_cnsts.value[i]); - parents[i] = n_parents.value[i]; + res.cnsts[i] = Expr.create(this, n_cnsts.value[i]); + res.parents[i] = n_parents.value[i]; } for (int i = 0; i < num_theory; i++) - theory[i] = Expr.create(this, n_theory.value[i]); - return r; + res.theory[i] = Expr.create(this, n_theory.value[i]); + return res; } - + /// /// Writes an interpolation problem to a file. /// diff --git a/src/api/java/Model.java b/src/api/java/Model.java index 22d232006..e9922ac58 100644 --- a/src/api/java/Model.java +++ b/src/api/java/Model.java @@ -273,11 +273,7 @@ public class Model extends Z3Object ASTVector nUniv = new ASTVector(getContext(), Native.modelGetSortUniverse( getContext().nCtx(), getNativeObject(), s.getNativeObject())); - int n = nUniv.size(); - Expr[] res = new Expr[n]; - for (int i = 0; i < n; i++) - res[i] = Expr.create(getContext(), nUniv.get(i).getNativeObject()); - return res; + return nUniv.ToExprArray(); } /** diff --git a/src/api/java/Solver.java b/src/api/java/Solver.java index e4ba9de42..4d2d9b641 100644 --- a/src/api/java/Solver.java +++ b/src/api/java/Solver.java @@ -176,8 +176,7 @@ public class Solver extends Z3Object **/ public int getNumAssertions() { - ASTVector assrts = new ASTVector(getContext(), Native.solverGetAssertions( - getContext().nCtx(), getNativeObject())); + ASTVector assrts = new ASTVector(getContext(), Native.solverGetAssertions(getContext().nCtx(), getNativeObject())); return assrts.size(); } @@ -188,13 +187,8 @@ public class Solver extends Z3Object **/ public BoolExpr[] getAssertions() { - ASTVector assrts = new ASTVector(getContext(), Native.solverGetAssertions( - getContext().nCtx(), getNativeObject())); - int n = assrts.size(); - BoolExpr[] res = new BoolExpr[n]; - for (int i = 0; i < n; i++) - res[i] = new BoolExpr(getContext(), assrts.get(i).getNativeObject()); - return res; + ASTVector assrts = new ASTVector(getContext(), Native.solverGetAssertions(getContext().nCtx(), getNativeObject())); + return assrts.ToBoolExprArray(); } /** @@ -282,16 +276,11 @@ public class Solver extends Z3Object * * @throws Z3Exception **/ - public Expr[] getUnsatCore() + public BoolExpr[] getUnsatCore() { - ASTVector core = new ASTVector(getContext(), Native.solverGetUnsatCore( - getContext().nCtx(), getNativeObject())); - int n = core.size(); - Expr[] res = new Expr[n]; - for (int i = 0; i < n; i++) - res[i] = Expr.create(getContext(), core.get(i).getNativeObject()); - return res; + ASTVector core = new ASTVector(getContext(), Native.solverGetUnsatCore(getContext().nCtx(), getNativeObject())); + return core.ToBoolExprArray(); } /** diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index a7cafbc29..51eb35e29 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -192,8 +192,59 @@ struct end -module AST = -struct +module rec AST : +sig + type ast = z3_native_object + val context_of_ast : ast -> 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 -> Expr.expr 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_expr_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) @@ -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,18 +2843,26 @@ 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 @@ -2790,7 +2873,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 +2882,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 +2898,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 +2930,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 +2942,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 +2958,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 +2985,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 +3051,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..e0f89565d 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 @@ -189,7 +195,7 @@ sig val get_size : ast_map -> int (** The keys stored in the map. *) - val get_keys : ast_map -> ast list + val get_keys : ast_map -> Expr.expr list (** Retrieves a string representation of the map.*) val to_string : ast_map -> string @@ -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 @@ -3198,6 +3212,19 @@ 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 (** Functions for handling SMT and SMT2 expressions and files *) @@ -3272,12 +3299,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 +3382,5 @@ val enable_trace : string -> unit *) val disable_trace : string -> unit + + diff --git a/src/api/ml/z3_stubs.c b/src/api/ml/z3_stubs.c new file mode 100644 index 000000000..7222158bb --- /dev/null +++ b/src/api/ml/z3_stubs.c @@ -0,0 +1,18978 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +/* File generated from z3.idl */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef Custom_tag +#include +#include +#endif +#include + + +#include "z3.h" + +#define xstr(s) str(s) +#define str(s) #s +#pragma warning(disable:4090) + +void check_error_code (Z3_context c); + +Z3_context last_ctx; + + + + value caml_final_register (value f, value v); + + void register_finalizer(value** closure, char* name, Z3_context ctx, value v) + { + if (*closure == NULL) { + *closure = caml_named_value(name); + if (*closure == NULL) { + Z3_set_error(ctx, Z3_INTERNAL_FATAL); + return; + } + } + caml_final_register(**closure, v); + } + + value c2ml_Z3_context (Z3_context* c) + { + static value* finalize_Z3_context_closure = NULL; + value v; + v = caml_alloc_small(1, Abstract_tag); + Field(v, 0) = (value) *c; + register_finalizer(&finalize_Z3_context_closure, "finalize_Z3_context", + (Z3_context) *c, v); + return v; + } + + void ml2c_Z3_context (value v, Z3_context* c) + { + *c = (Z3_context) Field(v, 0); + last_ctx = *c; + } + + value finalize_Z3_context (value v) + { + Z3_context c; + c = (Z3_context) Field(v, 0); + Z3_del_context(c); + return Val_unit; + } + +#define camlidl_ml2c_z3_Z3_context(v,c,ctx) ml2c_Z3_context(v,c) + +#define camlidl_c2ml_z3_Z3_context(c,ctx) c2ml_Z3_context(c) + +void camlidl_ml2c_z3_Z3_symbol(value _v1, Z3_symbol * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_symbol *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3_Z3_symbol(Z3_symbol * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_symbol) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_symbol *) Bp_val(_v1)) = *_c2; + return _v1; +} + + +typedef struct _Z3_ast_context { + Z3_ast ast; + Z3_context ctx; +} Z3_ast_context; + +void ml2c_Z3_ast (value v, Z3_ast* c) +{ + *c = ((Z3_ast_context*) Data_custom_val(v))->ast; +} + +static int compare_Z3_ast (value v1, value v2) +{ + Z3_ast_context* ac1; + Z3_ast_context* ac2; + unsigned int id1, id2; + ac1 = Data_custom_val(v1); + ac2 = Data_custom_val(v2); + id1 = Z3_get_ast_id(ac1->ctx, ac1->ast); + check_error_code(ac1->ctx); + id2 = Z3_get_ast_id(ac2->ctx, ac2->ast); + check_error_code(ac2->ctx); + return id2 - id1; +} + +static intnat hash_Z3_ast (value v) +{ + Z3_ast_context* ac; + unsigned int hash; + ac = Data_custom_val(v); + hash = Z3_get_ast_hash(ac->ctx, ac->ast); + check_error_code(ac->ctx); + return hash; +} + + + value finalize_Z3_ast (value v) + { + Z3_ast_context* ac; + ac = Data_custom_val(v); + Z3_dec_ref(ac->ctx, ac->ast); + check_error_code(ac->ctx); + return Val_unit; + } + + static struct custom_operations cops_Z3_ast = { + NULL, + custom_finalize_default, + compare_Z3_ast, + hash_Z3_ast, + custom_serialize_default, + custom_deserialize_default + }; + + value c2ml_Z3_ast (Z3_ast* c) + { + static value* finalize_Z3_ast_closure = NULL; + value v; + Z3_ast_context* ac; + check_error_code(last_ctx); + v = caml_alloc_custom(&cops_Z3_ast, sizeof(Z3_ast_context), 0, 1); + ac = Data_custom_val(v); + ac->ast = *c; + ac->ctx = last_ctx; + register_finalizer(&finalize_Z3_ast_closure, "finalize_Z3_ast", + (Z3_context) *c, v); + Z3_inc_ref(last_ctx, *c); + return v; + } + +#define camlidl_ml2c_z3_Z3_ast(v,c,ctx) ml2c_Z3_ast(v,c) + +#define camlidl_c2ml_z3_Z3_ast(c,ctx) c2ml_Z3_ast(c) + +#define DEFINE_SUBAST_OPS(T) void ml2c_ ## T (value v, T * a) { ml2c_Z3_ast(v, (Z3_ast*) a); } value c2ml_ ## T (T * a) { return c2ml_Z3_ast((Z3_ast*) a); } +DEFINE_SUBAST_OPS(Z3_sort) +#define camlidl_ml2c_z3_Z3_sort(v,c,ctx) ml2c_Z3_sort(v,c) + +#define camlidl_c2ml_z3_Z3_sort(c,ctx) c2ml_Z3_sort(c) + +DEFINE_SUBAST_OPS(Z3_func_decl) +#define camlidl_ml2c_z3_Z3_func_decl(v,c,ctx) ml2c_Z3_func_decl(v,c) + +#define camlidl_c2ml_z3_Z3_func_decl(c,ctx) c2ml_Z3_func_decl(c) + +DEFINE_SUBAST_OPS(Z3_app) +#define camlidl_ml2c_z3_Z3_app(v,c,ctx) ml2c_Z3_app(v,c) + +#define camlidl_c2ml_z3_Z3_app(c,ctx) c2ml_Z3_app(c) + +DEFINE_SUBAST_OPS(Z3_pattern) +#define camlidl_ml2c_z3_Z3_pattern(v,c,ctx) ml2c_Z3_pattern(v,c) + +#define camlidl_c2ml_z3_Z3_pattern(c,ctx) c2ml_Z3_pattern(c) + +#define DEFINE_RC_OPS(T) value c2ml_ ## T (T * c) { static value* finalize_ ## T ## _closure = NULL; value v; check_error_code(last_ctx); v = caml_alloc_small(2, Abstract_tag); Field(v, 0) = (value) *c; Field(v, 1) = (value) last_ctx; register_finalizer(&finalize_ ## T ## _closure, xstr(finalize_ ## T), (Z3_context) *c, v); T ## _inc_ref(last_ctx, *c); return v; } void ml2c_ ## T (value v, T * c) { *c = (T) Field(v, 0); } value finalize_ ## T (value v) { Z3_context c; c = (Z3_context) Field(v, 1); T ## _dec_ref(c, (T) Field(v, 0)); check_error_code(c); return Val_unit; } +DEFINE_RC_OPS(Z3_params) +#define camlidl_ml2c_z3_Z3_params(v,c,ctx) ml2c_Z3_params(v,c) + +#define camlidl_c2ml_z3_Z3_params(c,ctx) c2ml_Z3_params(c) + +DEFINE_RC_OPS(Z3_param_descrs) +#define camlidl_ml2c_z3_Z3_param_descrs(v,c,ctx) ml2c_Z3_param_descrs(v,c) + +#define camlidl_c2ml_z3_Z3_param_descrs(c,ctx) c2ml_Z3_param_descrs(c) + +DEFINE_RC_OPS(Z3_model) +#define camlidl_ml2c_z3_Z3_model(v,c,ctx) ml2c_Z3_model(v,c) + +#define camlidl_c2ml_z3_Z3_model(c,ctx) c2ml_Z3_model(c) + +DEFINE_RC_OPS(Z3_func_interp) +#define camlidl_ml2c_z3_Z3_func_interp(v,c,ctx) ml2c_Z3_func_interp(v,c) + +#define camlidl_c2ml_z3_Z3_func_interp(c,ctx) c2ml_Z3_func_interp(c) + +DEFINE_RC_OPS(Z3_func_entry) +#define camlidl_ml2c_z3_Z3_func_entry(v,c,ctx) ml2c_Z3_func_entry(v,c) + +#define camlidl_c2ml_z3_Z3_func_entry(c,ctx) c2ml_Z3_func_entry(c) + +DEFINE_RC_OPS(Z3_fixedpoint) +#define camlidl_ml2c_z3_Z3_fixedpoint(v,c,ctx) ml2c_Z3_fixedpoint(v,c) + +#define camlidl_c2ml_z3_Z3_fixedpoint(c,ctx) c2ml_Z3_fixedpoint(c) + +DEFINE_RC_OPS(Z3_ast_vector) +#define camlidl_ml2c_z3_Z3_ast_vector(v,c,ctx) ml2c_Z3_ast_vector(v,c) + +#define camlidl_c2ml_z3_Z3_ast_vector(c,ctx) c2ml_Z3_ast_vector(c) + +DEFINE_RC_OPS(Z3_ast_map) +#define camlidl_ml2c_z3_Z3_ast_map(v,c,ctx) ml2c_Z3_ast_map(v,c) + +#define camlidl_c2ml_z3_Z3_ast_map(c,ctx) c2ml_Z3_ast_map(c) + +DEFINE_RC_OPS(Z3_goal) +#define camlidl_ml2c_z3_Z3_goal(v,c,ctx) ml2c_Z3_goal(v,c) + +#define camlidl_c2ml_z3_Z3_goal(c,ctx) c2ml_Z3_goal(c) + +DEFINE_RC_OPS(Z3_tactic) +#define camlidl_ml2c_z3_Z3_tactic(v,c,ctx) ml2c_Z3_tactic(v,c) + +#define camlidl_c2ml_z3_Z3_tactic(c,ctx) c2ml_Z3_tactic(c) + +DEFINE_RC_OPS(Z3_probe) +#define camlidl_ml2c_z3_Z3_probe(v,c,ctx) ml2c_Z3_probe(v,c) + +#define camlidl_c2ml_z3_Z3_probe(c,ctx) c2ml_Z3_probe(c) + +DEFINE_RC_OPS(Z3_apply_result) +#define camlidl_ml2c_z3_Z3_apply_result(v,c,ctx) ml2c_Z3_apply_result(v,c) + +#define camlidl_c2ml_z3_Z3_apply_result(c,ctx) c2ml_Z3_apply_result(c) + +DEFINE_RC_OPS(Z3_solver) +#define camlidl_ml2c_z3_Z3_solver(v,c,ctx) ml2c_Z3_solver(v,c) + +#define camlidl_c2ml_z3_Z3_solver(c,ctx) c2ml_Z3_solver(c) + +DEFINE_RC_OPS(Z3_stats) +#define camlidl_ml2c_z3_Z3_stats(v,c,ctx) ml2c_Z3_stats(v,c) + +#define camlidl_c2ml_z3_Z3_stats(c,ctx) c2ml_Z3_stats(c) + +#define DEFINE_OPT_OPS(T) void ml2c_ ## T ## _opt (value v, T* c) { struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; if (v != Val_int(0)) { camlidl_ml2c_z3_ ## T(Field(v, 0), c, _ctx); } else { *c = NULL; } } value c2ml_ ## T ## _opt (T* c) { struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; camlidl_ctx _ctx = &_ctxs; value v; value a; if (*c) { a = camlidl_c2ml_z3_ ## T(c, _ctx); Begin_root(a) v = caml_alloc_small(1, 0); Field(v, 0) = a; End_roots(); } else { v = Val_int(0); } return v; } + +DEFINE_OPT_OPS(Z3_ast) +#define camlidl_ml2c_z3_Z3_ast_opt(v,c,ctx) ml2c_Z3_ast_opt(v,c) + +#define camlidl_c2ml_z3_Z3_ast_opt(c,ctx) c2ml_Z3_ast_opt(c) + +DEFINE_OPT_OPS(Z3_sort) +#define camlidl_ml2c_z3_Z3_sort_opt(v,c,ctx) ml2c_Z3_sort_opt(v,c) + +#define camlidl_c2ml_z3_Z3_sort_opt(c,ctx) c2ml_Z3_sort_opt(c) + +DEFINE_OPT_OPS(Z3_func_interp) +#define camlidl_ml2c_z3_Z3_func_interp_opt(v,c,ctx) ml2c_Z3_func_interp_opt(v,c) + +#define camlidl_c2ml_z3_Z3_func_interp_opt(c,ctx) c2ml_Z3_func_interp_opt(c) + +void camlidl_ml2c_z3_Z3_constructor(value _v1, Z3_constructor * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_constructor *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3_Z3_constructor(Z3_constructor * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_constructor) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_constructor *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3_Z3_constructor_list(value _v1, Z3_constructor_list * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_constructor_list *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3_Z3_constructor_list(Z3_constructor_list * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_constructor_list) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_constructor_list *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3_Z3_string(value _v1, Z3_string * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_malloc_string(_v1, _ctx); +} + +value camlidl_c2ml_z3_Z3_string(Z3_string * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = copy_string((*_c2)); + return _v1; +} + +int camlidl_transl_table_z3_enum_1[3] = { + Z3_L_FALSE, + Z3_L_UNDEF, + Z3_L_TRUE, +}; + +void camlidl_ml2c_z3_Z3_lbool(value _v1, Z3_lbool * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_1[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_lbool(Z3_lbool * _c2, camlidl_ctx _ctx) +{ +value _v1; + switch((*_c2)) { + case Z3_L_FALSE: _v1 = Val_int(0); break; + case Z3_L_UNDEF: _v1 = Val_int(1); break; + case Z3_L_TRUE: _v1 = Val_int(2); break; + default: invalid_argument("typedef Z3_lbool: bad enum value"); + } + return _v1; +} + +int camlidl_transl_table_z3_enum_2[2] = { + Z3_INT_SYMBOL, + Z3_STRING_SYMBOL, +}; + +void camlidl_ml2c_z3_Z3_symbol_kind(value _v1, Z3_symbol_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_2[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_symbol_kind(Z3_symbol_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + switch((*_c2)) { + case Z3_INT_SYMBOL: _v1 = Val_int(0); break; + case Z3_STRING_SYMBOL: _v1 = Val_int(1); break; + default: invalid_argument("typedef Z3_symbol_kind: bad enum value"); + } + return _v1; +} + +int camlidl_transl_table_z3_enum_3[7] = { + Z3_PARAMETER_INT, + Z3_PARAMETER_DOUBLE, + Z3_PARAMETER_RATIONAL, + Z3_PARAMETER_SYMBOL, + Z3_PARAMETER_SORT, + Z3_PARAMETER_AST, + Z3_PARAMETER_FUNC_DECL, +}; + +void camlidl_ml2c_z3_Z3_parameter_kind(value _v1, Z3_parameter_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_3[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_parameter_kind(Z3_parameter_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_3, 7, "typedef Z3_parameter_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3_enum_4[10] = { + Z3_UNINTERPRETED_SORT, + Z3_BOOL_SORT, + Z3_INT_SORT, + Z3_REAL_SORT, + Z3_BV_SORT, + Z3_ARRAY_SORT, + Z3_DATATYPE_SORT, + Z3_RELATION_SORT, + Z3_FINITE_DOMAIN_SORT, + Z3_UNKNOWN_SORT, +}; + +void camlidl_ml2c_z3_Z3_sort_kind(value _v1, Z3_sort_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_4[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_sort_kind(Z3_sort_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_4, 10, "typedef Z3_sort_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3_enum_5[7] = { + Z3_NUMERAL_AST, + Z3_APP_AST, + Z3_VAR_AST, + Z3_QUANTIFIER_AST, + Z3_SORT_AST, + Z3_FUNC_DECL_AST, + Z3_UNKNOWN_AST, +}; + +void camlidl_ml2c_z3_Z3_ast_kind(value _v1, Z3_ast_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_5[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_ast_kind(Z3_ast_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_5, 7, "typedef Z3_ast_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3_enum_6[152] = { + Z3_OP_TRUE, + Z3_OP_FALSE, + Z3_OP_EQ, + Z3_OP_DISTINCT, + Z3_OP_ITE, + Z3_OP_AND, + Z3_OP_OR, + Z3_OP_IFF, + Z3_OP_XOR, + Z3_OP_NOT, + Z3_OP_IMPLIES, + Z3_OP_OEQ, + Z3_OP_ANUM, + Z3_OP_AGNUM, + Z3_OP_LE, + Z3_OP_GE, + Z3_OP_LT, + Z3_OP_GT, + Z3_OP_ADD, + Z3_OP_SUB, + Z3_OP_UMINUS, + Z3_OP_MUL, + Z3_OP_DIV, + Z3_OP_IDIV, + Z3_OP_REM, + Z3_OP_MOD, + Z3_OP_TO_REAL, + Z3_OP_TO_INT, + Z3_OP_IS_INT, + Z3_OP_POWER, + Z3_OP_STORE, + Z3_OP_SELECT, + Z3_OP_CONST_ARRAY, + Z3_OP_ARRAY_MAP, + Z3_OP_ARRAY_DEFAULT, + Z3_OP_SET_UNION, + Z3_OP_SET_INTERSECT, + Z3_OP_SET_DIFFERENCE, + Z3_OP_SET_COMPLEMENT, + Z3_OP_SET_SUBSET, + Z3_OP_AS_ARRAY, + Z3_OP_BNUM, + Z3_OP_BIT1, + Z3_OP_BIT0, + Z3_OP_BNEG, + Z3_OP_BADD, + Z3_OP_BSUB, + Z3_OP_BMUL, + Z3_OP_BSDIV, + Z3_OP_BUDIV, + Z3_OP_BSREM, + Z3_OP_BUREM, + Z3_OP_BSMOD, + Z3_OP_BSDIV0, + Z3_OP_BUDIV0, + Z3_OP_BSREM0, + Z3_OP_BUREM0, + Z3_OP_BSMOD0, + Z3_OP_ULEQ, + Z3_OP_SLEQ, + Z3_OP_UGEQ, + Z3_OP_SGEQ, + Z3_OP_ULT, + Z3_OP_SLT, + Z3_OP_UGT, + Z3_OP_SGT, + Z3_OP_BAND, + Z3_OP_BOR, + Z3_OP_BNOT, + Z3_OP_BXOR, + Z3_OP_BNAND, + Z3_OP_BNOR, + Z3_OP_BXNOR, + Z3_OP_CONCAT, + Z3_OP_SIGN_EXT, + Z3_OP_ZERO_EXT, + Z3_OP_EXTRACT, + Z3_OP_REPEAT, + Z3_OP_BREDOR, + Z3_OP_BREDAND, + Z3_OP_BCOMP, + Z3_OP_BSHL, + Z3_OP_BLSHR, + Z3_OP_BASHR, + Z3_OP_ROTATE_LEFT, + Z3_OP_ROTATE_RIGHT, + Z3_OP_EXT_ROTATE_LEFT, + Z3_OP_EXT_ROTATE_RIGHT, + Z3_OP_INT2BV, + Z3_OP_BV2INT, + Z3_OP_CARRY, + Z3_OP_XOR3, + Z3_OP_PR_UNDEF, + Z3_OP_PR_TRUE, + Z3_OP_PR_ASSERTED, + Z3_OP_PR_GOAL, + Z3_OP_PR_MODUS_PONENS, + Z3_OP_PR_REFLEXIVITY, + Z3_OP_PR_SYMMETRY, + Z3_OP_PR_TRANSITIVITY, + Z3_OP_PR_TRANSITIVITY_STAR, + Z3_OP_PR_MONOTONICITY, + Z3_OP_PR_QUANT_INTRO, + Z3_OP_PR_DISTRIBUTIVITY, + Z3_OP_PR_AND_ELIM, + Z3_OP_PR_NOT_OR_ELIM, + Z3_OP_PR_REWRITE, + Z3_OP_PR_REWRITE_STAR, + Z3_OP_PR_PULL_QUANT, + Z3_OP_PR_PULL_QUANT_STAR, + Z3_OP_PR_PUSH_QUANT, + Z3_OP_PR_ELIM_UNUSED_VARS, + Z3_OP_PR_DER, + Z3_OP_PR_QUANT_INST, + Z3_OP_PR_HYPOTHESIS, + Z3_OP_PR_LEMMA, + Z3_OP_PR_UNIT_RESOLUTION, + Z3_OP_PR_IFF_TRUE, + Z3_OP_PR_IFF_FALSE, + Z3_OP_PR_COMMUTATIVITY, + Z3_OP_PR_DEF_AXIOM, + Z3_OP_PR_DEF_INTRO, + Z3_OP_PR_APPLY_DEF, + Z3_OP_PR_IFF_OEQ, + Z3_OP_PR_NNF_POS, + Z3_OP_PR_NNF_NEG, + Z3_OP_PR_NNF_STAR, + Z3_OP_PR_CNF_STAR, + Z3_OP_PR_SKOLEMIZE, + Z3_OP_PR_MODUS_PONENS_OEQ, + Z3_OP_PR_TH_LEMMA, + Z3_OP_PR_HYPER_RESOLVE, + Z3_OP_RA_STORE, + Z3_OP_RA_EMPTY, + Z3_OP_RA_IS_EMPTY, + Z3_OP_RA_JOIN, + Z3_OP_RA_UNION, + Z3_OP_RA_WIDEN, + Z3_OP_RA_PROJECT, + Z3_OP_RA_FILTER, + Z3_OP_RA_NEGATION_FILTER, + Z3_OP_RA_RENAME, + Z3_OP_RA_COMPLEMENT, + Z3_OP_RA_SELECT, + Z3_OP_RA_CLONE, + Z3_OP_FD_LT, + Z3_OP_LABEL, + Z3_OP_LABEL_LIT, + Z3_OP_DT_CONSTRUCTOR, + Z3_OP_DT_RECOGNISER, + Z3_OP_DT_ACCESSOR, + Z3_OP_UNINTERPRETED, +}; + +void camlidl_ml2c_z3_Z3_decl_kind(value _v1, Z3_decl_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_6[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_decl_kind(Z3_decl_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_6, 152, "typedef Z3_decl_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3_enum_7[7] = { + Z3_PK_UINT, + Z3_PK_BOOL, + Z3_PK_DOUBLE, + Z3_PK_SYMBOL, + Z3_PK_STRING, + Z3_PK_OTHER, + Z3_PK_INVALID, +}; + +void camlidl_ml2c_z3_Z3_param_kind(value _v1, Z3_param_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_7[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_param_kind(Z3_param_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_7, 7, "typedef Z3_param_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3_enum_8[4] = { + Z3_PRINT_SMTLIB_FULL, + Z3_PRINT_LOW_LEVEL, + Z3_PRINT_SMTLIB_COMPLIANT, + Z3_PRINT_SMTLIB2_COMPLIANT, +}; + +void camlidl_ml2c_z3_Z3_ast_print_mode(value _v1, Z3_ast_print_mode * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_8[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_ast_print_mode(Z3_ast_print_mode * _c2, camlidl_ctx _ctx) +{ +value _v1; + switch((*_c2)) { + case Z3_PRINT_SMTLIB_FULL: _v1 = Val_int(0); break; + case Z3_PRINT_LOW_LEVEL: _v1 = Val_int(1); break; + case Z3_PRINT_SMTLIB_COMPLIANT: _v1 = Val_int(2); break; + case Z3_PRINT_SMTLIB2_COMPLIANT: _v1 = Val_int(3); break; + default: invalid_argument("typedef Z3_ast_print_mode: bad enum value"); + } + return _v1; +} + +int camlidl_transl_table_z3_enum_9[13] = { + Z3_OK, + Z3_SORT_ERROR, + Z3_IOB, + Z3_INVALID_ARG, + Z3_PARSER_ERROR, + Z3_NO_PARSER, + Z3_INVALID_PATTERN, + Z3_MEMOUT_FAIL, + Z3_FILE_ACCESS_ERROR, + Z3_INTERNAL_FATAL, + Z3_INVALID_USAGE, + Z3_DEC_REF_ERROR, + Z3_EXCEPTION, +}; + +void camlidl_ml2c_z3_Z3_error_code(value _v1, Z3_error_code * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_9[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_error_code(Z3_error_code * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3_enum_9, 13, "typedef Z3_error_code: bad enum value"); + return _v1; +} + + +value camlidl_c2ml_z3_Z3_error_code(Z3_error_code * _c2, camlidl_ctx _ctx); + + +void check_error_code (Z3_context c) +{ + static struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + value* exn_tag = NULL; + value ctx_err[2]; + Z3_error_code e; + e = Z3_get_error_code(c); + if (e != Z3_OK) { + ctx_err[0] = c2ml_Z3_context(&c); + ctx_err[1] = camlidl_c2ml_z3_Z3_error_code(&e, &_ctxs); + exn_tag = caml_named_value("Z3.Error"); + if (*exn_tag == 0) { + fprintf(stderr, "Z3.Error not found"); + exit(1); + } + caml_raise_with_args(*exn_tag, 2, ctx_err); + } +} + + +void* error_handler_static = NULL; + +int camlidl_transl_table_z3_enum_10[4] = { + Z3_GOAL_PRECISE, + Z3_GOAL_UNDER, + Z3_GOAL_OVER, + Z3_GOAL_UNDER_OVER, +}; + +void camlidl_ml2c_z3_Z3_goal_prec(value _v1, Z3_goal_prec * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3_enum_10[Int_val(_v1)]; +} + +value camlidl_c2ml_z3_Z3_goal_prec(Z3_goal_prec * _c2, camlidl_ctx _ctx) +{ +value _v1; + switch((*_c2)) { + case Z3_GOAL_PRECISE: _v1 = Val_int(0); break; + case Z3_GOAL_UNDER: _v1 = Val_int(1); break; + case Z3_GOAL_OVER: _v1 = Val_int(2); break; + case Z3_GOAL_UNDER_OVER: _v1 = Val_int(3); break; + default: invalid_argument("typedef Z3_goal_prec: bad enum value"); + } + return _v1; +} + + +value caml_z3_mk_context(value key_val_list) +{ + CAMLparam1( key_val_list ); + CAMLlocal4( item, vkey, vval, _vres ); + char * ckey; + char * cval; + Z3_config cfg; + Z3_context _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + + cfg = Z3_mk_config(); + + while (key_val_list != Val_emptylist) + { + item = Field(key_val_list, 0); + vkey = Field(item, 0); + vval = Field(item, 1); + ckey = camlidl_malloc_string(vkey, _ctx); + cval = camlidl_malloc_string(vval, _ctx); + Z3_set_param_value(cfg, ckey, cval); + key_val_list = Field(key_val_list, 1); + } + + _res = Z3_mk_context_rc(cfg); + Z3_del_config(cfg); + _vres = camlidl_c2ml_z3_Z3_context(&_res, _ctx); + camlidl_free(_ctx); + Z3_set_error_handler(_res, error_handler_static); + CAMLreturn(_vres); +} + +value camlidl_z3_Z3_update_param_value( + value _v_c, + value _v_param_id, + value _v_param_value) +{ + Z3_context c; /*in*/ + Z3_string param_id; /*in*/ + Z3_string param_value; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_param_id, ¶m_id, _ctx); + camlidl_ml2c_z3_Z3_string(_v_param_value, ¶m_value, _ctx); + Z3_update_param_value(c, param_id, param_value); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_get_param_value( + value _v_c, + value _v_param_id) +{ + Z3_context c; /*in*/ + Z3_string param_id; /*in*/ + Z3_string *param_value; /*out*/ + Z3_string _c1; + value _v2; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_param_id, ¶m_id, _ctx); + param_value = &_c1; + Z3_get_param_value(c, param_id, param_value); + if (param_value == NULL) { + _vres = Val_int(0); + } else { + _v2 = camlidl_c2ml_z3_Z3_string(&*param_value, _ctx); + Begin_root(_v2) + _vres = camlidl_alloc_small(1, 0); + Field(_vres, 0) = _v2; + End_roots(); + } + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_interrupt( + value _v_c) +{ + Z3_context c; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + Z3_interrupt(c); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_mk_params( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_params _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_params(c); + _vres = camlidl_c2ml_z3_Z3_params(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_params_set_bool( + value _v_c, + value _v_p, + value _v_k, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_params p; /*in*/ + Z3_symbol k; /*in*/ + int v; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_k, &k, _ctx); + v = Int_val(_v_v); + Z3_params_set_bool(c, p, k, v); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_params_set_uint( + value _v_c, + value _v_p, + value _v_k, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_params p; /*in*/ + Z3_symbol k; /*in*/ + unsigned int v; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_k, &k, _ctx); + v = Int_val(_v_v); + Z3_params_set_uint(c, p, k, v); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_params_set_double( + value _v_c, + value _v_p, + value _v_k, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_params p; /*in*/ + Z3_symbol k; /*in*/ + double v; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_k, &k, _ctx); + v = Double_val(_v_v); + Z3_params_set_double(c, p, k, v); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_params_set_symbol( + value _v_c, + value _v_p, + value _v_k, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_params p; /*in*/ + Z3_symbol k; /*in*/ + Z3_symbol v; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_k, &k, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_v, &v, _ctx); + Z3_params_set_symbol(c, p, k, v); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_params_to_string( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_params p; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + _res = Z3_params_to_string(c, p); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_params_validate( + value _v_c, + value _v_p, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_params p; /*in*/ + Z3_param_descrs d; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_param_descrs(_v_d, &d, _ctx); + Z3_params_validate(c, p, d); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_param_descrs_get_kind( + value _v_c, + value _v_p, + value _v_n) +{ + Z3_context c; /*in*/ + Z3_param_descrs p; /*in*/ + Z3_symbol n; /*in*/ + Z3_param_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_param_descrs(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_n, &n, _ctx); + _res = Z3_param_descrs_get_kind(c, p, n); + _vres = camlidl_c2ml_z3_Z3_param_kind(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_param_descrs_size( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_param_descrs p; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_param_descrs(_v_p, &p, _ctx); + _res = Z3_param_descrs_size(c, p); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_param_descrs_get_name( + value _v_c, + value _v_p, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_param_descrs p; /*in*/ + unsigned int i; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_param_descrs(_v_p, &p, _ctx); + i = Int_val(_v_i); + _res = Z3_param_descrs_get_name(c, p, i); + _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_param_descrs_to_string( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_param_descrs p; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_param_descrs(_v_p, &p, _ctx); + _res = Z3_param_descrs_to_string(c, p); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_int_symbol( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + int i; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_mk_int_symbol(c, i); + _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_string_symbol( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_string s; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_s, &s, _ctx); + _res = Z3_mk_string_symbol(c, s); + _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_uninterpreted_sort( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_mk_uninterpreted_sort(c, s); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bool_sort( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_bool_sort(c); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_int_sort( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_int_sort(c); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_real_sort( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_real_sort(c); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bv_sort( + value _v_c, + value _v_sz) +{ + Z3_context c; /*in*/ + unsigned int sz; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + sz = Int_val(_v_sz); + _res = Z3_mk_bv_sort(c, sz); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_finite_domain_sort( + value _v_c, + value _v_name, + value _v_size) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + __int64 size; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); + size = Int64_val(_v_size); + _res = Z3_mk_finite_domain_sort(c, name, size); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_array_sort( + value _v_c, + value _v_domain, + value _v_range) +{ + Z3_context c; /*in*/ + Z3_sort domain; /*in*/ + Z3_sort range; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_domain, &domain, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_range, &range, _ctx); + _res = Z3_mk_array_sort(c, domain, range); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_tuple_sort( + value _v_c, + value _v_mk_tuple_name, + value _v_field_names, + value _v_field_sorts) +{ + Z3_context c; /*in*/ + Z3_symbol mk_tuple_name; /*in*/ + unsigned int num_fields; /*in*/ + Z3_symbol const *field_names; /*in*/ + Z3_sort const *field_sorts; /*in*/ + Z3_func_decl *mk_tuple_decl; /*out*/ + Z3_func_decl *proj_decl; /*out*/ + Z3_sort _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + Z3_func_decl _c7; + mlsize_t _c8; + value _v9; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_mk_tuple_name, &mk_tuple_name, _ctx); + _c1 = Wosize_val(_v_field_names); + field_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_field_names, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &field_names[_c2], _ctx); + } + num_fields = _c1; + _c4 = Wosize_val(_v_field_sorts); + field_sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_field_sorts, _c5); + camlidl_ml2c_z3_Z3_sort(_v6, &field_sorts[_c5], _ctx); + } + num_fields = _c4; + mk_tuple_decl = &_c7; + proj_decl = camlidl_malloc(num_fields * sizeof(Z3_func_decl ), _ctx); + _res = Z3_mk_tuple_sort(c, mk_tuple_name, num_fields, field_names, field_sorts, mk_tuple_decl, proj_decl); + Begin_roots_block(_vres, 3) + _vres[0] = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + _vres[1] = camlidl_c2ml_z3_Z3_func_decl(&*mk_tuple_decl, _ctx); + _vres[2] = camlidl_alloc(num_fields, 0); + Begin_root(_vres[2]) + for (_c8 = 0; _c8 < num_fields; _c8++) { + _v9 = camlidl_c2ml_z3_Z3_func_decl(&proj_decl[_c8], _ctx); + modify(&Field(_vres[2], _c8), _v9); + } + End_roots() + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_mk_enumeration_sort( + value _v_c, + value _v_name, + value _v_enum_names) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + unsigned int n; /*in*/ + Z3_symbol const *enum_names; /*in*/ + Z3_func_decl *enum_consts; /*out*/ + Z3_func_decl *enum_testers; /*out*/ + Z3_sort _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + value _v5; + mlsize_t _c6; + value _v7; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); + _c1 = Wosize_val(_v_enum_names); + enum_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_enum_names, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &enum_names[_c2], _ctx); + } + n = _c1; + enum_consts = camlidl_malloc(n * sizeof(Z3_func_decl ), _ctx); + enum_testers = camlidl_malloc(n * sizeof(Z3_func_decl ), _ctx); + _res = Z3_mk_enumeration_sort(c, name, n, enum_names, enum_consts, enum_testers); + Begin_roots_block(_vres, 3) + _vres[0] = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + _vres[1] = camlidl_alloc(n, 0); + Begin_root(_vres[1]) + for (_c4 = 0; _c4 < n; _c4++) { + _v5 = camlidl_c2ml_z3_Z3_func_decl(&enum_consts[_c4], _ctx); + modify(&Field(_vres[1], _c4), _v5); + } + End_roots() + _vres[2] = camlidl_alloc(n, 0); + Begin_root(_vres[2]) + for (_c6 = 0; _c6 < n; _c6++) { + _v7 = camlidl_c2ml_z3_Z3_func_decl(&enum_testers[_c6], _ctx); + modify(&Field(_vres[2], _c6), _v7); + } + End_roots() + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_mk_list_sort( + value _v_c, + value _v_name, + value _v_elem_sort) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + Z3_sort elem_sort; /*in*/ + 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; /*out*/ + Z3_sort _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + Z3_func_decl _c1; + Z3_func_decl _c2; + Z3_func_decl _c3; + Z3_func_decl _c4; + Z3_func_decl _c5; + Z3_func_decl _c6; + value _vresult; + value _vres[7] = { 0, 0, 0, 0, 0, 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_elem_sort, &elem_sort, _ctx); + nil_decl = &_c1; + is_nil_decl = &_c2; + cons_decl = &_c3; + is_cons_decl = &_c4; + head_decl = &_c5; + tail_decl = &_c6; + _res = Z3_mk_list_sort(c, name, elem_sort, nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl); + Begin_roots_block(_vres, 7) + _vres[0] = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + _vres[1] = camlidl_c2ml_z3_Z3_func_decl(&*nil_decl, _ctx); + _vres[2] = camlidl_c2ml_z3_Z3_func_decl(&*is_nil_decl, _ctx); + _vres[3] = camlidl_c2ml_z3_Z3_func_decl(&*cons_decl, _ctx); + _vres[4] = camlidl_c2ml_z3_Z3_func_decl(&*is_cons_decl, _ctx); + _vres[5] = camlidl_c2ml_z3_Z3_func_decl(&*head_decl, _ctx); + _vres[6] = camlidl_c2ml_z3_Z3_func_decl(&*tail_decl, _ctx); + _vresult = camlidl_alloc_small(7, 0); + { mlsize_t _c7; + for (_c7 = 0; _c7 < 7; _c7++) Field(_vresult, _c7) = _vres[_c7]; + } + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_mk_constructor( + value _v_c, + value _v_name, + value _v_recognizer, + value _v_field_names, + value _v_sorts, + value _v_sort_refs) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + Z3_symbol recognizer; /*in*/ + unsigned int num_fields; /*in*/ + Z3_symbol const *field_names; /*in*/ + Z3_sort_opt const *sorts; /*in*/ + unsigned int *sort_refs; /*in*/ + Z3_constructor _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_recognizer, &recognizer, _ctx); + _c1 = Wosize_val(_v_field_names); + field_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_field_names, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &field_names[_c2], _ctx); + } + num_fields = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort_opt const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3_Z3_sort_opt(_v6, &sorts[_c5], _ctx); + } + num_fields = _c4; + _c7 = Wosize_val(_v_sort_refs); + sort_refs = camlidl_malloc(_c7 * sizeof(unsigned int ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_sort_refs, _c8); + sort_refs[_c8] = Int_val(_v9); + } + num_fields = _c7; + _res = Z3_mk_constructor(c, name, recognizer, num_fields, field_names, sorts, sort_refs); + _vres = camlidl_c2ml_z3_Z3_constructor(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_constructor_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_mk_constructor(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3_Z3_del_constructor( + value _v_c, + value _v_constr) +{ + Z3_context c; /*in*/ + Z3_constructor constr; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_constructor(_v_constr, &constr, _ctx); + Z3_del_constructor(c, constr); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_mk_datatype( + value _v_c, + value _v_name, + value _v_constructors) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + unsigned int num_constructors; /*in*/ + Z3_constructor *constructors; /*in,out*/ + Z3_sort _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + value _v5; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); + _c1 = Wosize_val(_v_constructors); + constructors = camlidl_malloc(_c1 * sizeof(Z3_constructor ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_constructors, _c2); + camlidl_ml2c_z3_Z3_constructor(_v3, &constructors[_c2], _ctx); + } + num_constructors = _c1; + _res = Z3_mk_datatype(c, name, num_constructors, constructors); + Begin_roots_block(_vres, 2) + _vres[0] = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + _vres[1] = camlidl_alloc(num_constructors, 0); + Begin_root(_vres[1]) + for (_c4 = 0; _c4 < num_constructors; _c4++) { + _v5 = camlidl_c2ml_z3_Z3_constructor(&constructors[_c4], _ctx); + modify(&Field(_vres[1], _c4), _v5); + } + End_roots() + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_mk_constructor_list( + value _v_c, + value _v_constructors) +{ + Z3_context c; /*in*/ + unsigned int num_constructors; /*in*/ + Z3_constructor const *constructors; /*in*/ + Z3_constructor_list _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_constructors); + constructors = camlidl_malloc(_c1 * sizeof(Z3_constructor const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_constructors, _c2); + camlidl_ml2c_z3_Z3_constructor(_v3, &constructors[_c2], _ctx); + } + num_constructors = _c1; + _res = Z3_mk_constructor_list(c, num_constructors, constructors); + _vres = camlidl_c2ml_z3_Z3_constructor_list(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_del_constructor_list( + value _v_c, + value _v_clist) +{ + Z3_context c; /*in*/ + Z3_constructor_list clist; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_constructor_list(_v_clist, &clist, _ctx); + Z3_del_constructor_list(c, clist); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_mk_datatypes( + value _v_c, + value _v_sort_names, + value _v_constructor_lists) +{ + Z3_context c; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort *sorts; /*out*/ + Z3_constructor_list *constructor_lists; /*in,out*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + value _v8; + mlsize_t _c9; + value _v10; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_constructor_lists); + constructor_lists = camlidl_malloc(_c4 * sizeof(Z3_constructor_list ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_constructor_lists, _c5); + camlidl_ml2c_z3_Z3_constructor_list(_v6, &constructor_lists[_c5], _ctx); + } + num_sorts = _c4; + sorts = camlidl_malloc(num_sorts * sizeof(Z3_sort ), _ctx); + Z3_mk_datatypes(c, num_sorts, sort_names, sorts, constructor_lists); + Begin_roots_block(_vres, 2) + _vres[0] = camlidl_alloc(num_sorts, 0); + Begin_root(_vres[0]) + for (_c7 = 0; _c7 < num_sorts; _c7++) { + _v8 = camlidl_c2ml_z3_Z3_sort(&sorts[_c7], _ctx); + modify(&Field(_vres[0], _c7), _v8); + } + End_roots() + _vres[1] = camlidl_alloc(num_sorts, 0); + Begin_root(_vres[1]) + for (_c9 = 0; _c9 < num_sorts; _c9++) { + _v10 = camlidl_c2ml_z3_Z3_constructor_list(&constructor_lists[_c9], _ctx); + modify(&Field(_vres[1], _c9), _v10); + } + End_roots() + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_query_constructor( + value _v_c, + value _v_constr, + value _v_num_fields) +{ + Z3_context c; /*in*/ + Z3_constructor constr; /*in*/ + unsigned int num_fields; /*in*/ + Z3_func_decl *constructor; /*out*/ + Z3_func_decl *tester; /*out*/ + Z3_func_decl *accessors; /*out*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + Z3_func_decl _c1; + Z3_func_decl _c2; + mlsize_t _c3; + value _v4; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_constructor(_v_constr, &constr, _ctx); + num_fields = Int_val(_v_num_fields); + constructor = &_c1; + tester = &_c2; + accessors = camlidl_malloc(num_fields * sizeof(Z3_func_decl ), _ctx); + Z3_query_constructor(c, constr, num_fields, constructor, tester, accessors); + Begin_roots_block(_vres, 3) + _vres[0] = camlidl_c2ml_z3_Z3_func_decl(&*constructor, _ctx); + _vres[1] = camlidl_c2ml_z3_Z3_func_decl(&*tester, _ctx); + _vres[2] = camlidl_alloc(num_fields, 0); + Begin_root(_vres[2]) + for (_c3 = 0; _c3 < num_fields; _c3++) { + _v4 = camlidl_c2ml_z3_Z3_func_decl(&accessors[_c3], _ctx); + modify(&Field(_vres[2], _c3), _v4); + } + End_roots() + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_mk_func_decl( + value _v_c, + value _v_s, + value _v_domain, + value _v_range) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + unsigned int domain_size; /*in*/ + Z3_sort const *domain; /*in*/ + Z3_sort range; /*in*/ + Z3_func_decl _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); + _c1 = Wosize_val(_v_domain); + domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_domain, _c2); + camlidl_ml2c_z3_Z3_sort(_v3, &domain[_c2], _ctx); + } + domain_size = _c1; + camlidl_ml2c_z3_Z3_sort(_v_range, &range, _ctx); + _res = Z3_mk_func_decl(c, s, domain_size, domain, range); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_app( + value _v_c, + value _v_d, + value _v_args) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_app(c, d, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_const( + value _v_c, + value _v_s, + value _v_ty) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_const(c, s, ty); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_fresh_func_decl( + value _v_c, + value _v_prefix, + value _v_domain, + value _v_range) +{ + Z3_context c; /*in*/ + Z3_string prefix; /*in*/ + unsigned int domain_size; /*in*/ + Z3_sort const *domain; /*in*/ + Z3_sort range; /*in*/ + Z3_func_decl _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_prefix, &prefix, _ctx); + _c1 = Wosize_val(_v_domain); + domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_domain, _c2); + camlidl_ml2c_z3_Z3_sort(_v3, &domain[_c2], _ctx); + } + domain_size = _c1; + camlidl_ml2c_z3_Z3_sort(_v_range, &range, _ctx); + _res = Z3_mk_fresh_func_decl(c, prefix, domain_size, domain, range); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_fresh_const( + value _v_c, + value _v_prefix, + value _v_ty) +{ + Z3_context c; /*in*/ + Z3_string prefix; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_prefix, &prefix, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_fresh_const(c, prefix, ty); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_true( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_true(c); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_false( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_false(c); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_eq( + value _v_c, + value _v_l, + value _v_r) +{ + Z3_context c; /*in*/ + Z3_ast l; /*in*/ + Z3_ast r; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_l, &l, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_r, &r, _ctx); + _res = Z3_mk_eq(c, l, r); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_distinct( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_distinct(c, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_not( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_mk_not(c, a); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_ite( + value _v_c, + value _v_t1, + value _v_t2, + value _v_t3) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast t3; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t3, &t3, _ctx); + _res = Z3_mk_ite(c, t1, t2, t3); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_iff( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_iff(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_implies( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_implies(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_xor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_xor(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_and( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_and(c, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_or( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_or(c, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_add( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_add(c, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_mul( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_mul(c, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_sub( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_sub(c, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_unary_minus( + value _v_c, + value _v_arg) +{ + Z3_context c; /*in*/ + Z3_ast arg; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg, &arg, _ctx); + _res = Z3_mk_unary_minus(c, arg); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_div( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_div(c, arg1, arg2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_mod( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_mod(c, arg1, arg2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_rem( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_rem(c, arg1, arg2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_power( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_power(c, arg1, arg2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_lt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_lt(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_le( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_le(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_gt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_gt(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_ge( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_ge(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_int2real( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_int2real(c, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_real2int( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_real2int(c, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_is_int( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_is_int(c, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvnot( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvnot(c, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvredand( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvredand(c, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvredor( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvredor(c, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvand( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvand(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvor(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvxor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvxor(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvnand( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvnand(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvnor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvnor(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvxnor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvxnor(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvneg( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvneg(c, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvadd( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvadd(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsub( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsub(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvmul( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvmul(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvudiv( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvudiv(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsdiv( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsdiv(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvurem( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvurem(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsrem( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsrem(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsmod( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsmod(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvult( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvult(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvslt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvslt(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvule( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvule(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsle( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsle(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvuge( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvuge(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsge( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsge(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvugt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvugt(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsgt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsgt(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_concat( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_concat(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_extract( + value _v_c, + value _v_high, + value _v_low, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int high; /*in*/ + unsigned int low; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + high = Int_val(_v_high); + low = Int_val(_v_low); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_extract(c, high, low, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_sign_ext( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_sign_ext(c, i, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_zero_ext( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_zero_ext(c, i, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_repeat( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_repeat(c, i, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvshl( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvshl(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvlshr( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvlshr(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvashr( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvashr(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_rotate_left( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_rotate_left(c, i, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_rotate_right( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_rotate_right(c, i, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_ext_rotate_left( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_ext_rotate_left(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_ext_rotate_right( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_ext_rotate_right(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_int2bv( + value _v_c, + value _v_n, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int n; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + n = Int_val(_v_n); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_int2bv(c, n, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bv2int( + value _v_c, + value _v_t1, + value _v_is_signed) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + int is_signed; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + is_signed = Int_val(_v_is_signed); + _res = Z3_mk_bv2int(c, t1, is_signed); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvadd_no_overflow( + value _v_c, + value _v_t1, + value _v_t2, + value _v_is_signed) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + int is_signed; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + is_signed = Int_val(_v_is_signed); + _res = Z3_mk_bvadd_no_overflow(c, t1, t2, is_signed); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvadd_no_underflow( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvadd_no_underflow(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsub_no_overflow( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsub_no_overflow(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsub_no_underflow( + value _v_c, + value _v_t1, + value _v_t2, + value _v_is_signed) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + int is_signed; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + is_signed = Int_val(_v_is_signed); + _res = Z3_mk_bvsub_no_underflow(c, t1, t2, is_signed); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvsdiv_no_overflow( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsdiv_no_overflow(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvneg_no_overflow( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvneg_no_overflow(c, t1); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvmul_no_overflow( + value _v_c, + value _v_t1, + value _v_t2, + value _v_is_signed) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + int is_signed; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + is_signed = Int_val(_v_is_signed); + _res = Z3_mk_bvmul_no_overflow(c, t1, t2, is_signed); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bvmul_no_underflow( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvmul_no_underflow(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_select( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_i, &i, _ctx); + _res = Z3_mk_select(c, a, i); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_store( + value _v_c, + value _v_a, + value _v_i, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast i; /*in*/ + Z3_ast v; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_i, &i, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); + _res = Z3_mk_store(c, a, i, v); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_const_array( + value _v_c, + value _v_domain, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_sort domain; /*in*/ + Z3_ast v; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_domain, &domain, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); + _res = Z3_mk_const_array(c, domain, v); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_map( + value _v_c, + value _v_f, + value _v_n, + value _v_args) +{ + Z3_context c; /*in*/ + Z3_func_decl f; /*in*/ + unsigned int n; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + Z3_ast _c1; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); + n = Int_val(_v_n); + args = &_c1; + camlidl_ml2c_z3_Z3_ast(_v_args, &_c1, _ctx); + _res = Z3_mk_map(c, f, n, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_array_default( + value _v_c, + value _v_array) +{ + Z3_context c; /*in*/ + Z3_ast array; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_array, &array, _ctx); + _res = Z3_mk_array_default(c, array); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_sort( + value _v_c, + value _v_ty) +{ + Z3_context c; /*in*/ + Z3_sort ty; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_set_sort(c, ty); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_empty_set( + value _v_c, + value _v_domain) +{ + Z3_context c; /*in*/ + Z3_sort domain; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_domain, &domain, _ctx); + _res = Z3_mk_empty_set(c, domain); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_full_set( + value _v_c, + value _v_domain) +{ + Z3_context c; /*in*/ + Z3_sort domain; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_domain, &domain, _ctx); + _res = Z3_mk_full_set(c, domain); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_add( + value _v_c, + value _v_set, + value _v_elem) +{ + Z3_context c; /*in*/ + Z3_ast set; /*in*/ + Z3_ast elem; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_set, &set, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_elem, &elem, _ctx); + _res = Z3_mk_set_add(c, set, elem); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_del( + value _v_c, + value _v_set, + value _v_elem) +{ + Z3_context c; /*in*/ + Z3_ast set; /*in*/ + Z3_ast elem; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_set, &set, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_elem, &elem, _ctx); + _res = Z3_mk_set_del(c, set, elem); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_union( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_set_union(c, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_intersect( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_set_intersect(c, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_difference( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_set_difference(c, arg1, arg2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_complement( + value _v_c, + value _v_arg) +{ + Z3_context c; /*in*/ + Z3_ast arg; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg, &arg, _ctx); + _res = Z3_mk_set_complement(c, arg); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_member( + value _v_c, + value _v_elem, + value _v_set) +{ + Z3_context c; /*in*/ + Z3_ast elem; /*in*/ + Z3_ast set; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_elem, &elem, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_set, &set, _ctx); + _res = Z3_mk_set_member(c, elem, set); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_set_subset( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_set_subset(c, arg1, arg2); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_numeral( + value _v_c, + value _v_numeral, + value _v_ty) +{ + Z3_context c; /*in*/ + Z3_string numeral; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_numeral, &numeral, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_numeral(c, numeral, ty); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_real( + value _v_c, + value _v_num, + value _v_den) +{ + Z3_context c; /*in*/ + int num; /*in*/ + int den; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + num = Int_val(_v_num); + den = Int_val(_v_den); + _res = Z3_mk_real(c, num, den); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_int( + value _v_c, + value _v_v, + value _v_ty) +{ + Z3_context c; /*in*/ + int v; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + v = Int_val(_v_v); + camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_int(c, v, ty); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_int64( + value _v_c, + value _v_v, + value _v_ty) +{ + Z3_context c; /*in*/ + __int64 v; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + v = Int64_val(_v_v); + camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_int64(c, v, ty); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_pattern( + value _v_c, + value _v_terms) +{ + Z3_context c; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_ast const *terms; /*in*/ + Z3_pattern _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_terms); + terms = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_terms, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &terms[_c2], _ctx); + } + num_patterns = _c1; + _res = Z3_mk_pattern(c, num_patterns, terms); + _vres = camlidl_c2ml_z3_Z3_pattern(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_bound( + value _v_c, + value _v_index, + value _v_ty) +{ + Z3_context c; /*in*/ + unsigned int index; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + index = Int_val(_v_index); + camlidl_ml2c_z3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_bound(c, index, ty); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_forall( + value _v_c, + value _v_weight, + value _v_patterns, + value _v_sorts, + value _v_decl_names, + value _v_body) +{ + Z3_context c; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_decls; /*in*/ + Z3_sort const *sorts; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_patterns, _c2); + camlidl_ml2c_z3_Z3_pattern(_v3, &patterns[_c2], _ctx); + } + num_patterns = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_decls = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_forall(c, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_forall_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_mk_forall(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3_Z3_mk_exists( + value _v_c, + value _v_weight, + value _v_patterns, + value _v_sorts, + value _v_decl_names, + value _v_body) +{ + Z3_context c; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_decls; /*in*/ + Z3_sort const *sorts; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_patterns, _c2); + camlidl_ml2c_z3_Z3_pattern(_v3, &patterns[_c2], _ctx); + } + num_patterns = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_decls = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_exists(c, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_exists_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_mk_exists(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3_Z3_mk_quantifier( + value _v_c, + value _v_is_forall, + value _v_weight, + value _v_patterns, + value _v_sorts, + value _v_decl_names, + value _v_body) +{ + Z3_context c; /*in*/ + int is_forall; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_decls; /*in*/ + Z3_sort const *sorts; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + is_forall = Int_val(_v_is_forall); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_patterns, _c2); + camlidl_ml2c_z3_Z3_pattern(_v3, &patterns[_c2], _ctx); + } + num_patterns = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_decls = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_quantifier(c, is_forall, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_quantifier_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_mk_quantifier(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); +} + +value camlidl_z3_Z3_mk_quantifier_ex( + value _v_c, + value _v_is_forall, + value _v_weight, + value _v_quantifier_id, + value _v_skolem_id, + value _v_patterns, + value _v_no_patterns, + value _v_sorts, + value _v_decl_names, + value _v_body) +{ + Z3_context c; /*in*/ + int is_forall; /*in*/ + unsigned int weight; /*in*/ + Z3_symbol quantifier_id; /*in*/ + Z3_symbol skolem_id; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_no_patterns; /*in*/ + Z3_ast const *no_patterns; /*in*/ + unsigned int num_decls; /*in*/ + Z3_sort const *sorts; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + is_forall = Int_val(_v_is_forall); + weight = Int_val(_v_weight); + camlidl_ml2c_z3_Z3_symbol(_v_quantifier_id, &quantifier_id, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_skolem_id, &skolem_id, _ctx); + _c1 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_patterns, _c2); + camlidl_ml2c_z3_Z3_pattern(_v3, &patterns[_c2], _ctx); + } + num_patterns = _c1; + _c4 = Wosize_val(_v_no_patterns); + no_patterns = camlidl_malloc(_c4 * sizeof(Z3_ast const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_no_patterns, _c5); + camlidl_ml2c_z3_Z3_ast(_v6, &no_patterns[_c5], _ctx); + } + num_no_patterns = _c4; + _c7 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c7 * sizeof(Z3_sort const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_sorts, _c8); + camlidl_ml2c_z3_Z3_sort(_v9, &sorts[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c10 * sizeof(Z3_symbol const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decl_names, _c11); + camlidl_ml2c_z3_Z3_symbol(_v12, &decl_names[_c11], _ctx); + } + num_decls = _c10; + camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_quantifier_ex(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, patterns, num_no_patterns, no_patterns, num_decls, sorts, decl_names, body); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_quantifier_ex_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_mk_quantifier_ex(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); +} + +value camlidl_z3_Z3_mk_forall_const( + value _v_c, + value _v_weight, + value _v_bound, + value _v_patterns, + value _v_body) +{ + Z3_context c; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_bound; /*in*/ + Z3_app const *bound; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_bound); + bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_bound, _c2); + camlidl_ml2c_z3_Z3_app(_v3, &bound[_c2], _ctx); + } + num_bound = _c1; + _c4 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_patterns, _c5); + camlidl_ml2c_z3_Z3_pattern(_v6, &patterns[_c5], _ctx); + } + num_patterns = _c4; + camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_forall_const(c, weight, num_bound, bound, num_patterns, patterns, body); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_exists_const( + value _v_c, + value _v_weight, + value _v_bound, + value _v_patterns, + value _v_body) +{ + Z3_context c; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_bound; /*in*/ + Z3_app const *bound; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_bound); + bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_bound, _c2); + camlidl_ml2c_z3_Z3_app(_v3, &bound[_c2], _ctx); + } + num_bound = _c1; + _c4 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_patterns, _c5); + camlidl_ml2c_z3_Z3_pattern(_v6, &patterns[_c5], _ctx); + } + num_patterns = _c4; + camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_exists_const(c, weight, num_bound, bound, num_patterns, patterns, body); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_quantifier_const( + value _v_c, + value _v_is_forall, + value _v_weight, + value _v_bound, + value _v_patterns, + value _v_body) +{ + Z3_context c; /*in*/ + int is_forall; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_bound; /*in*/ + Z3_app const *bound; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + is_forall = Int_val(_v_is_forall); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_bound); + bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_bound, _c2); + camlidl_ml2c_z3_Z3_app(_v3, &bound[_c2], _ctx); + } + num_bound = _c1; + _c4 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_patterns, _c5); + camlidl_ml2c_z3_Z3_pattern(_v6, &patterns[_c5], _ctx); + } + num_patterns = _c4; + camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_quantifier_const(c, is_forall, weight, num_bound, bound, num_patterns, patterns, body); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_quantifier_const_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_mk_quantifier_const(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3_Z3_mk_quantifier_const_ex( + value _v_c, + value _v_is_forall, + value _v_weight, + value _v_quantifier_id, + value _v_skolem_id, + value _v_bound, + value _v_patterns, + value _v_no_patterns, + value _v_body) +{ + Z3_context c; /*in*/ + int is_forall; /*in*/ + unsigned int weight; /*in*/ + Z3_symbol quantifier_id; /*in*/ + Z3_symbol skolem_id; /*in*/ + unsigned int num_bound; /*in*/ + Z3_app const *bound; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_no_patterns; /*in*/ + Z3_ast const *no_patterns; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + is_forall = Int_val(_v_is_forall); + weight = Int_val(_v_weight); + camlidl_ml2c_z3_Z3_symbol(_v_quantifier_id, &quantifier_id, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_skolem_id, &skolem_id, _ctx); + _c1 = Wosize_val(_v_bound); + bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_bound, _c2); + camlidl_ml2c_z3_Z3_app(_v3, &bound[_c2], _ctx); + } + num_bound = _c1; + _c4 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_patterns, _c5); + camlidl_ml2c_z3_Z3_pattern(_v6, &patterns[_c5], _ctx); + } + num_patterns = _c4; + _c7 = Wosize_val(_v_no_patterns); + no_patterns = camlidl_malloc(_c7 * sizeof(Z3_ast const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_no_patterns, _c8); + camlidl_ml2c_z3_Z3_ast(_v9, &no_patterns[_c8], _ctx); + } + num_no_patterns = _c7; + camlidl_ml2c_z3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_quantifier_const_ex(c, is_forall, weight, quantifier_id, skolem_id, num_bound, bound, num_patterns, patterns, num_no_patterns, no_patterns, body); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_quantifier_const_ex_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_mk_quantifier_const_ex(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); +} + +value camlidl_z3_Z3_get_symbol_kind( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + Z3_symbol_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_get_symbol_kind(c, s); + _vres = camlidl_c2ml_z3_Z3_symbol_kind(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_symbol_int( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_get_symbol_int(c, s); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_symbol_string( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_get_symbol_string(c, s); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_sort_name( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_sort d; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_d, &d, _ctx); + _res = Z3_get_sort_name(c, d); + _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_sort_id( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_get_sort_id(c, s); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_sort_to_ast( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_sort_to_ast(c, s); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_eq_sort( + value _v_c, + value _v_s1, + value _v_s2) +{ + Z3_context c; /*in*/ + Z3_sort s1; /*in*/ + Z3_sort s2; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s1, &s1, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s2, &s2, _ctx); + _res = Z3_is_eq_sort(c, s1, s2); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_sort_kind( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + Z3_sort_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_sort_kind(c, t); + _vres = camlidl_c2ml_z3_Z3_sort_kind(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_bv_sort_size( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_bv_sort_size(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_finite_domain_sort_size( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + __int64 *r; /*out*/ + __int64 _c1; + value _v2; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); + r = &_c1; + Z3_get_finite_domain_sort_size(c, s, r); + if (r == NULL) { + _vres = Val_int(0); + } else { + _v2 = copy_int64(*r); + Begin_root(_v2) + _vres = camlidl_alloc_small(1, 0); + Field(_vres, 0) = _v2; + End_roots(); + } + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_array_sort_domain( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_array_sort_domain(c, t); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_array_sort_range( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_array_sort_range(c, t); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_tuple_sort_mk_decl( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_tuple_sort_mk_decl(c, t); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_tuple_sort_num_fields( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_tuple_sort_num_fields(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_tuple_sort_field_decl( + value _v_c, + value _v_t, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int i; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + i = Int_val(_v_i); + _res = Z3_get_tuple_sort_field_decl(c, t, i); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_datatype_sort_num_constructors( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_datatype_sort_num_constructors(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_datatype_sort_constructor( + value _v_c, + value _v_t, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int idx; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_datatype_sort_constructor(c, t, idx); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_datatype_sort_recognizer( + value _v_c, + value _v_t, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int idx; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_datatype_sort_recognizer(c, t, idx); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_datatype_sort_constructor_accessor( + value _v_c, + value _v_t, + value _v_idx_c, + value _v_idx_a) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int idx_c; /*in*/ + unsigned int idx_a; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_t, &t, _ctx); + idx_c = Int_val(_v_idx_c); + idx_a = Int_val(_v_idx_a); + _res = Z3_get_datatype_sort_constructor_accessor(c, t, idx_c, idx_a); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_relation_arity( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_get_relation_arity(c, s); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_relation_column( + value _v_c, + value _v_s, + value _v_col) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + unsigned int col; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); + col = Int_val(_v_col); + _res = Z3_get_relation_column(c, s, col); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_decl_to_ast( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_func_decl f; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); + _res = Z3_func_decl_to_ast(c, f); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_eq_func_decl( + value _v_c, + value _v_f1, + value _v_f2) +{ + Z3_context c; /*in*/ + Z3_func_decl f1; /*in*/ + Z3_func_decl f2; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_f1, &f1, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_f2, &f2, _ctx); + _res = Z3_is_eq_func_decl(c, f1, f2); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_func_decl_id( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_func_decl f; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); + _res = Z3_get_func_decl_id(c, f); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_name( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_decl_name(c, d); + _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_kind( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + Z3_decl_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_decl_kind(c, d); + _vres = camlidl_c2ml_z3_Z3_decl_kind(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_domain_size( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_domain_size(c, d); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_arity( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_arity(c, d); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_domain( + value _v_c, + value _v_d, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int i; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + i = Int_val(_v_i); + _res = Z3_get_domain(c, d, i); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_range( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_range(c, d); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_num_parameters( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_decl_num_parameters(c, d); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_parameter_kind( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_parameter_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_parameter_kind(c, d, idx); + _vres = camlidl_c2ml_z3_Z3_parameter_kind(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_int_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_int_parameter(c, d, idx); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_double_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + double _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_double_parameter(c, d, idx); + _vres = copy_double(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_symbol_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_symbol_parameter(c, d, idx); + _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_sort_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_sort_parameter(c, d, idx); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_ast_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_ast_parameter(c, d, idx); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_func_decl_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_func_decl_parameter(c, d, idx); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_decl_rational_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_rational_parameter(c, d, idx); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_app_to_ast( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_app a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_app(_v_a, &a, _ctx); + _res = Z3_app_to_ast(c, a); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_app_decl( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_app a; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_app(_v_a, &a, _ctx); + _res = Z3_get_app_decl(c, a); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_app_num_args( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_app a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_app(_v_a, &a, _ctx); + _res = Z3_get_app_num_args(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_app_arg( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_app a; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_app(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_app_arg(c, a, i); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_eq_ast( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_is_eq_ast(c, t1, t2); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_ast_id( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_ast t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t, &t, _ctx); + _res = Z3_get_ast_id(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_ast_hash( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_ast_hash(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_sort( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_sort(c, a); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_well_sorted( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_ast t; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t, &t, _ctx); + _res = Z3_is_well_sorted(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_bool_value( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_lbool _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_bool_value(c, a); + _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_ast_kind( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_ast_kind(c, a); + _vres = camlidl_c2ml_z3_Z3_ast_kind(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_app( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_app(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_numeral_ast( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_numeral_ast(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_algebraic_number( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_algebraic_number(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_to_app( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_app _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_to_app(c, a); + _vres = camlidl_c2ml_z3_Z3_app(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_to_func_decl( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_to_func_decl(c, a); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_numeral_string( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_numeral_string(c, a); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_numeral_decimal_string( + value _v_c, + value _v_a, + value _v_precision) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int precision; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + precision = Int_val(_v_precision); + _res = Z3_get_numeral_decimal_string(c, a, precision); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_numerator( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_numerator(c, a); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_denominator( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_denominator(c, a); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_numeral_small( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + __int64 *num; /*out*/ + __int64 *den; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + __int64 _c1; + __int64 _c2; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + num = &_c1; + den = &_c2; + _res = Z3_get_numeral_small(c, a, num, den); + Begin_roots_block(_vres, 3) + _vres[0] = Val_int(_res); + _vres[1] = copy_int64(*num); + _vres[2] = copy_int64(*den); + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_get_numeral_int( + value _v_c, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast v; /*in*/ + int *i; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + int _c1; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); + i = &_c1; + _res = Z3_get_numeral_int(c, v, i); + Begin_roots_block(_vres, 2) + _vres[0] = Val_int(_res); + _vres[1] = Val_int(*i); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_get_numeral_int64( + value _v_c, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast v; /*in*/ + __int64 *i; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + __int64 _c1; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); + i = &_c1; + _res = Z3_get_numeral_int64(c, v, i); + Begin_roots_block(_vres, 2) + _vres[0] = Val_int(_res); + _vres[1] = copy_int64(*i); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_get_numeral_rational_int64( + value _v_c, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast v; /*in*/ + __int64 *num; /*out*/ + __int64 *den; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + __int64 _c1; + __int64 _c2; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); + num = &_c1; + den = &_c2; + _res = Z3_get_numeral_rational_int64(c, v, num, den); + Begin_roots_block(_vres, 3) + _vres[0] = Val_int(_res); + _vres[1] = copy_int64(*num); + _vres[2] = copy_int64(*den); + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +value camlidl_z3_Z3_get_algebraic_number_lower( + value _v_c, + value _v_a, + value _v_precision) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int precision; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + precision = Int_val(_v_precision); + _res = Z3_get_algebraic_number_lower(c, a, precision); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_algebraic_number_upper( + value _v_c, + value _v_a, + value _v_precision) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int precision; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + precision = Int_val(_v_precision); + _res = Z3_get_algebraic_number_upper(c, a, precision); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_pattern_to_ast( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_pattern p; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_pattern(_v_p, &p, _ctx); + _res = Z3_pattern_to_ast(c, p); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_pattern_num_terms( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_pattern p; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_pattern(_v_p, &p, _ctx); + _res = Z3_get_pattern_num_terms(c, p); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_pattern( + value _v_c, + value _v_p, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_pattern p; /*in*/ + unsigned int idx; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_pattern(_v_p, &p, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_pattern(c, p, idx); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_index_value( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_index_value(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_quantifier_forall( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_quantifier_forall(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_weight( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_weight(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_num_patterns( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_num_patterns(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_pattern_ast( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int i; /*in*/ + Z3_pattern _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_quantifier_pattern_ast(c, a, i); + _vres = camlidl_c2ml_z3_Z3_pattern(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_num_no_patterns( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_num_no_patterns(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_no_pattern_ast( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_quantifier_no_pattern_ast(c, a, i); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_num_bound( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_num_bound(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_bound_name( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int i; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_quantifier_bound_name(c, a, i); + _vres = camlidl_c2ml_z3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_bound_sort( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int i; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_quantifier_bound_sort(c, a, i); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_quantifier_body( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_body(c, a); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_simplify( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_simplify(c, a); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_simplify_ex( + value _v_c, + value _v_a, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_params p; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + _res = Z3_simplify_ex(c, a, p); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_simplify_get_help( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_simplify_get_help(c); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_simplify_get_param_descrs( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_param_descrs _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_simplify_get_param_descrs(c); + _vres = camlidl_c2ml_z3_Z3_param_descrs(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_update_term( + value _v_c, + value _v_a, + value _v_args) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_update_term(c, a, num_args, args); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_substitute( + value _v_c, + value _v_a, + value _v_from, + value _v_to) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int num_exprs; /*in*/ + Z3_ast const *from; /*in*/ + Z3_ast const *to; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _c1 = Wosize_val(_v_from); + from = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_from, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &from[_c2], _ctx); + } + num_exprs = _c1; + _c4 = Wosize_val(_v_to); + to = camlidl_malloc(_c4 * sizeof(Z3_ast const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_to, _c5); + camlidl_ml2c_z3_Z3_ast(_v6, &to[_c5], _ctx); + } + num_exprs = _c4; + _res = Z3_substitute(c, a, num_exprs, from, to); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_substitute_vars( + value _v_c, + value _v_a, + value _v_to) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int num_exprs; /*in*/ + Z3_ast const *to; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _c1 = Wosize_val(_v_to); + to = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_to, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &to[_c2], _ctx); + } + num_exprs = _c1; + _res = Z3_substitute_vars(c, a, num_exprs, to); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_translate( + value _v_source, + value _v_a, + value _v_target) +{ + Z3_context source; /*in*/ + Z3_ast a; /*in*/ + Z3_context target; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_source, &source, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + camlidl_ml2c_z3_Z3_context(_v_target, &target, _ctx); + _res = Z3_translate(source, a, target); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(source); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_eval( + value _v_c, + value _v_m, + value _v_t, + value _v_model_completion) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_ast t; /*in*/ + int model_completion; /*in*/ + Z3_ast *v; /*out*/ + Z3_ast _c1; + value _v2; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_t, &t, _ctx); + model_completion = Int_val(_v_model_completion); + v = &_c1; + Z3_model_eval(c, m, t, model_completion, v); + if (v == NULL) { + _vres = Val_int(0); + } else { + _v2 = camlidl_c2ml_z3_Z3_ast(&*v, _ctx); + Begin_root(_v2) + _vres = camlidl_alloc_small(1, 0); + Field(_vres, 0) = _v2; + End_roots(); + } + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_const_interp( + value _v_c, + value _v_m, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_func_decl a; /*in*/ + Z3_ast_opt _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_a, &a, _ctx); + _res = Z3_model_get_const_interp(c, m, a); + _vres = camlidl_c2ml_z3_Z3_ast_opt(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_func_interp( + value _v_c, + value _v_m, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_func_decl f; /*in*/ + Z3_func_interp_opt _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); + _res = Z3_model_get_func_interp(c, m, f); + _vres = camlidl_c2ml_z3_Z3_func_interp_opt(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_num_consts( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + _res = Z3_model_get_num_consts(c, m); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_const_decl( + value _v_c, + value _v_m, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + _res = Z3_model_get_const_decl(c, m, i); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_num_funcs( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + _res = Z3_model_get_num_funcs(c, m); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_func_decl( + value _v_c, + value _v_m, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + _res = Z3_model_get_func_decl(c, m, i); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_num_sorts( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + _res = Z3_model_get_num_sorts(c, m); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_sort( + value _v_c, + value _v_m, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + _res = Z3_model_get_sort(c, m, i); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_get_sort_universe( + value _v_c, + value _v_m, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_sort s; /*in*/ + Z3_ast_vector _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_model_get_sort_universe(c, m, s); + _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_is_as_array( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_as_array(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_as_array_func_decl( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_as_array_func_decl(c, a); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_interp_get_num_entries( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_func_interp f; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_interp(_v_f, &f, _ctx); + _res = Z3_func_interp_get_num_entries(c, f); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_interp_get_entry( + value _v_c, + value _v_f, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_func_interp f; /*in*/ + unsigned int i; /*in*/ + Z3_func_entry _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_interp(_v_f, &f, _ctx); + i = Int_val(_v_i); + _res = Z3_func_interp_get_entry(c, f, i); + _vres = camlidl_c2ml_z3_Z3_func_entry(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_interp_get_else( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_func_interp f; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_interp(_v_f, &f, _ctx); + _res = Z3_func_interp_get_else(c, f); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_interp_get_arity( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_func_interp f; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_interp(_v_f, &f, _ctx); + _res = Z3_func_interp_get_arity(c, f); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_entry_get_value( + value _v_c, + value _v_e) +{ + Z3_context c; /*in*/ + Z3_func_entry e; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_entry(_v_e, &e, _ctx); + _res = Z3_func_entry_get_value(c, e); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_entry_get_num_args( + value _v_c, + value _v_e) +{ + Z3_context c; /*in*/ + Z3_func_entry e; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_entry(_v_e, &e, _ctx); + _res = Z3_func_entry_get_num_args(c, e); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_entry_get_arg( + value _v_c, + value _v_e, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_func_entry e; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_entry(_v_e, &e, _ctx); + i = Int_val(_v_i); + _res = Z3_func_entry_get_arg(c, e, i); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_open_log( + value _v_filename) +{ + Z3_string filename; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_string(_v_filename, &filename, _ctx); + _res = Z3_open_log(filename); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3_Z3_append_log( + value _v_string) +{ + Z3_string string; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_string(_v_string, &string, _ctx); + Z3_append_log(string); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3_Z3_close_log(value _unit) +{ + Z3_close_log(); + return Val_unit; +} + +value camlidl_z3_Z3_toggle_warning_messages( + value _v_enabled) +{ + int enabled; /*in*/ + enabled = Int_val(_v_enabled); + Z3_toggle_warning_messages(enabled); + return Val_unit; +} + +value camlidl_z3_Z3_set_ast_print_mode( + value _v_c, + value _v_mode) +{ + Z3_context c; /*in*/ + Z3_ast_print_mode mode; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_print_mode(_v_mode, &mode, _ctx); + Z3_set_ast_print_mode(c, mode); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_ast_to_string( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_ast_to_string(c, a); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_pattern_to_string( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_pattern p; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_pattern(_v_p, &p, _ctx); + _res = Z3_pattern_to_string(c, p); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_sort_to_string( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_sort_to_string(c, s); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_func_decl_to_string( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_func_decl_to_string(c, d); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_model_to_string( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + _res = Z3_model_to_string(c, m); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_benchmark_to_smtlib_string( + value _v_c, + value _v_name, + value _v_logic, + value _v_status, + value _v_attributes, + value _v_assumptions, + value _v_formula) +{ + Z3_context c; /*in*/ + Z3_string name; /*in*/ + Z3_string logic; /*in*/ + Z3_string status; /*in*/ + Z3_string attributes; /*in*/ + unsigned int num_assumptions; /*in*/ + Z3_ast const *assumptions; /*in*/ + Z3_ast formula; /*in*/ + Z3_string _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); + camlidl_ml2c_z3_Z3_string(_v_logic, &logic, _ctx); + camlidl_ml2c_z3_Z3_string(_v_status, &status, _ctx); + camlidl_ml2c_z3_Z3_string(_v_attributes, &attributes, _ctx); + _c1 = Wosize_val(_v_assumptions); + assumptions = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_assumptions, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &assumptions[_c2], _ctx); + } + num_assumptions = _c1; + camlidl_ml2c_z3_Z3_ast(_v_formula, &formula, _ctx); + _res = Z3_benchmark_to_smtlib_string(c, name, logic, status, attributes, num_assumptions, assumptions, formula); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_benchmark_to_smtlib_string_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_benchmark_to_smtlib_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); +} + +value camlidl_z3_Z3_parse_smtlib2_string( + value _v_c, + value _v_str, + value _v_sort_names, + value _v_sorts, + value _v_decl_names, + value _v_decls) +{ + Z3_context c; /*in*/ + Z3_string str; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int num_decls; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_func_decl const *decls; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_str, &str, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_sorts = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decls); + decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decls, _c11); + camlidl_ml2c_z3_Z3_func_decl(_v12, &decls[_c11], _ctx); + } + num_decls = _c10; + _res = Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_parse_smtlib2_string_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_parse_smtlib2_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3_Z3_parse_smtlib2_file( + value _v_c, + value _v_file_name, + value _v_sort_names, + value _v_sorts, + value _v_decl_names, + value _v_decls) +{ + Z3_context c; /*in*/ + Z3_string file_name; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int num_decls; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_func_decl const *decls; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_file_name, &file_name, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_sorts = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decls); + decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decls, _c11); + camlidl_ml2c_z3_Z3_func_decl(_v12, &decls[_c11], _ctx); + } + num_decls = _c10; + _res = Z3_parse_smtlib2_file(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_parse_smtlib2_file_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_parse_smtlib2_file(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3_Z3_parse_smtlib_string( + value _v_c, + value _v_str, + value _v_sort_names, + value _v_sorts, + value _v_decl_names, + value _v_decls) +{ + Z3_context c; /*in*/ + Z3_string str; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int num_decls; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_func_decl const *decls; /*in*/ + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_str, &str, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_sorts = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decls); + decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decls, _c11); + camlidl_ml2c_z3_Z3_func_decl(_v12, &decls[_c11], _ctx); + } + num_decls = _c10; + Z3_parse_smtlib_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_parse_smtlib_string_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_parse_smtlib_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3_Z3_parse_smtlib_file( + value _v_c, + value _v_file_name, + value _v_sort_names, + value _v_sorts, + value _v_decl_names, + value _v_decls) +{ + Z3_context c; /*in*/ + Z3_string file_name; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int num_decls; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_func_decl const *decls; /*in*/ + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_file_name, &file_name, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_sorts = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decls); + decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decls, _c11); + camlidl_ml2c_z3_Z3_func_decl(_v12, &decls[_c11], _ctx); + } + num_decls = _c10; + Z3_parse_smtlib_file(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_parse_smtlib_file_bytecode(value * argv, int argn) +{ + return camlidl_z3_Z3_parse_smtlib_file(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3_Z3_get_smtlib_num_formulas( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_num_formulas(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_smtlib_formula( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_smtlib_formula(c, i); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_smtlib_num_assumptions( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_num_assumptions(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_smtlib_assumption( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_smtlib_assumption(c, i); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_smtlib_num_decls( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_num_decls(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_smtlib_decl( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_smtlib_decl(c, i); + _vres = camlidl_c2ml_z3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_smtlib_num_sorts( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_num_sorts(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_smtlib_sort( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_smtlib_sort(c, i); + _vres = camlidl_c2ml_z3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_smtlib_error( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_error(c); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} +/* +value camlidl_z3_Z3_parse_z3_string( + value _v_c, + value _v_str) +{ + Z3_context c; /*in + Z3_string str; /*in + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_str, &str, _ctx); + _res = Z3_parse_z3_string(c, str); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence +check_error_code(c); + /* end user-supplied deallocation sequence + return _vres; +} + +value camlidl_z3_Z3_parse_z3_file( + value _v_c, + value _v_file_name) +{ + Z3_context c; /*in + Z3_string file_name; /*in + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_file_name, &file_name, _ctx); + _res = Z3_parse_z3_file(c, file_name); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence +check_error_code(c); + /* end user-supplied deallocation sequence + return _vres; +} +*/ +value camlidl_z3_Z3_set_error( + value _v_c, + value _v_e) +{ + Z3_context c; /*in*/ + Z3_error_code e; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_error_code(_v_e, &e, _ctx); + Z3_set_error(c, e); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_get_error_msg_ex( + value _v_c, + value _v_err) +{ + Z3_context c; /*in*/ + Z3_error_code err; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_error_code(_v_err, &err, _ctx); + _res = Z3_get_error_msg_ex(c, err); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_version(value _unit) +{ + unsigned int *major; /*out*/ + unsigned int *minor; /*out*/ + unsigned int *build_number; /*out*/ + unsigned int *revision_number; /*out*/ + unsigned int _c1; + unsigned int _c2; + unsigned int _c3; + unsigned int _c4; + value _vresult; + value _vres[4] = { 0, 0, 0, 0, }; + + major = &_c1; + minor = &_c2; + build_number = &_c3; + revision_number = &_c4; + Z3_get_version(major, minor, build_number, revision_number); + Begin_roots_block(_vres, 4) + _vres[0] = Val_int(*major); + _vres[1] = Val_int(*minor); + _vres[2] = Val_int(*build_number); + _vres[3] = Val_int(*revision_number); + _vresult = camlidl_alloc_small(4, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + Field(_vresult, 3) = _vres[3]; + End_roots() + return _vresult; +} + +value camlidl_z3_Z3_mk_fixedpoint( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_fixedpoint _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_fixedpoint(c); + _vres = camlidl_c2ml_z3_Z3_fixedpoint(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_add_rule( + value _v_c, + value _v_d, + value _v_rule, + value _v_name) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_ast rule; /*in*/ + Z3_symbol name; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_rule, &rule, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); + Z3_fixedpoint_add_rule(c, d, rule, name); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_add_fact( + value _v_c, + value _v_d, + value _v_r, + value _v_args) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_func_decl r; /*in*/ + unsigned int num_args; /*in*/ + unsigned int *args; /*in*/ + mlsize_t _c1; + mlsize_t _c2; + value _v3; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_r, &r, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(unsigned int ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + args[_c2] = Int_val(_v3); + } + num_args = _c1; + Z3_fixedpoint_add_fact(c, d, r, num_args, args); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_assert( + value _v_c, + value _v_d, + value _v_axiom) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_ast axiom; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_axiom, &axiom, _ctx); + Z3_fixedpoint_assert(c, d, axiom); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_query( + value _v_c, + value _v_d, + value _v_query) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_ast query; /*in*/ + Z3_lbool _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_query, &query, _ctx); + _res = Z3_fixedpoint_query(c, d, query); + _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_query_relations( + value _v_c, + value _v_d, + value _v_relations) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + unsigned int num_relations; /*in*/ + Z3_func_decl const *relations; /*in*/ + Z3_lbool _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + _c1 = Wosize_val(_v_relations); + relations = camlidl_malloc(_c1 * sizeof(Z3_func_decl const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_relations, _c2); + camlidl_ml2c_z3_Z3_func_decl(_v3, &relations[_c2], _ctx); + } + num_relations = _c1; + _res = Z3_fixedpoint_query_relations(c, d, num_relations, relations); + _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_get_answer( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + _res = Z3_fixedpoint_get_answer(c, d); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_get_reason_unknown( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + _res = Z3_fixedpoint_get_reason_unknown(c, d); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_update_rule( + value _v_c, + value _v_d, + value _v_a, + value _v_name) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_ast a; /*in*/ + Z3_symbol name; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_name, &name, _ctx); + Z3_fixedpoint_update_rule(c, d, a, name); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_get_num_levels( + value _v_c, + value _v_d, + value _v_pred) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_func_decl pred; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_pred, &pred, _ctx); + _res = Z3_fixedpoint_get_num_levels(c, d, pred); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_get_cover_delta( + value _v_c, + value _v_d, + value _v_level, + value _v_pred) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + int level; /*in*/ + Z3_func_decl pred; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + level = Int_val(_v_level); + camlidl_ml2c_z3_Z3_func_decl(_v_pred, &pred, _ctx); + _res = Z3_fixedpoint_get_cover_delta(c, d, level, pred); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_add_cover( + value _v_c, + value _v_d, + value _v_level, + value _v_pred, + value _v_property) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + int level; /*in*/ + Z3_func_decl pred; /*in*/ + Z3_ast property; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + level = Int_val(_v_level); + camlidl_ml2c_z3_Z3_func_decl(_v_pred, &pred, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_property, &property, _ctx); + Z3_fixedpoint_add_cover(c, d, level, pred, property); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_get_statistics( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_stats _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + _res = Z3_fixedpoint_get_statistics(c, d); + _vres = camlidl_c2ml_z3_Z3_stats(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_register_relation( + value _v_c, + value _v_d, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_func_decl f; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); + Z3_fixedpoint_register_relation(c, d, f); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_set_predicate_representation( + value _v_c, + value _v_d, + value _v_f, + value _v_relation_kinds) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + Z3_func_decl f; /*in*/ + unsigned int num_relations; /*in*/ + Z3_symbol const *relation_kinds; /*in*/ + mlsize_t _c1; + mlsize_t _c2; + value _v3; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + camlidl_ml2c_z3_Z3_func_decl(_v_f, &f, _ctx); + _c1 = Wosize_val(_v_relation_kinds); + relation_kinds = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_relation_kinds, _c2); + camlidl_ml2c_z3_Z3_symbol(_v3, &relation_kinds[_c2], _ctx); + } + num_relations = _c1; + Z3_fixedpoint_set_predicate_representation(c, d, f, num_relations, relation_kinds); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_simplify_rules( + value _v_c, + value _v_f, + value _v_rules, + value _v_outputs) +{ + Z3_context c; /*in*/ + Z3_fixedpoint f; /*in*/ + unsigned int num_rules; /*in*/ + Z3_ast *rules; /*in*/ + unsigned int num_outputs; /*in*/ + Z3_func_decl *outputs; /*in*/ + Z3_ast_vector _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); + _c1 = Wosize_val(_v_rules); + rules = camlidl_malloc(_c1 * sizeof(Z3_ast ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_rules, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &rules[_c2], _ctx); + } + num_rules = _c1; + _c4 = Wosize_val(_v_outputs); + outputs = camlidl_malloc(_c4 * sizeof(Z3_func_decl ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_outputs, _c5); + camlidl_ml2c_z3_Z3_func_decl(_v6, &outputs[_c5], _ctx); + } + num_outputs = _c4; + // _res = Z3_fixedpoint_simplify_rules(c, f, num_rules, rules, num_outputs, outputs); + _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_set_params( + value _v_c, + value _v_f, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_fixedpoint f; /*in*/ + Z3_params p; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + Z3_fixedpoint_set_params(c, f, p); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_get_help( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_fixedpoint f; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); + _res = Z3_fixedpoint_get_help(c, f); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_get_param_descrs( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_fixedpoint f; /*in*/ + Z3_param_descrs _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); + _res = Z3_fixedpoint_get_param_descrs(c, f); + _vres = camlidl_c2ml_z3_Z3_param_descrs(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_to_string( + value _v_c, + value _v_f, + value _v_queries) +{ + Z3_context c; /*in*/ + Z3_fixedpoint f; /*in*/ + unsigned int num_queries; /*in*/ + Z3_ast *queries; /*in*/ + Z3_string _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_f, &f, _ctx); + _c1 = Wosize_val(_v_queries); + queries = camlidl_malloc(_c1 * sizeof(Z3_ast ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_queries, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &queries[_c2], _ctx); + } + num_queries = _c1; + _res = Z3_fixedpoint_to_string(c, f, num_queries, queries); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_fixedpoint_push( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + Z3_fixedpoint_push(c, d); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_fixedpoint_pop( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_fixedpoint d; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_fixedpoint(_v_d, &d, _ctx); + Z3_fixedpoint_pop(c, d); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_mk_ast_vector( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_ast_vector _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_ast_vector(c); + _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_vector_size( + value _v_c, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast_vector v; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); + _res = Z3_ast_vector_size(c, v); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_vector_get( + value _v_c, + value _v_v, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast_vector v; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); + i = Int_val(_v_i); + _res = Z3_ast_vector_get(c, v, i); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_vector_set( + value _v_c, + value _v_v, + value _v_i, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast_vector v; /*in*/ + unsigned int i; /*in*/ + Z3_ast a; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + Z3_ast_vector_set(c, v, i, a); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_ast_vector_resize( + value _v_c, + value _v_v, + value _v_n) +{ + Z3_context c; /*in*/ + Z3_ast_vector v; /*in*/ + unsigned int n; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); + n = Int_val(_v_n); + Z3_ast_vector_resize(c, v, n); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_ast_vector_push( + value _v_c, + value _v_v, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast_vector v; /*in*/ + Z3_ast a; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + Z3_ast_vector_push(c, v, a); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_ast_vector_translate( + value _v_s, + value _v_v, + value _v_t) +{ + Z3_context s; /*in*/ + Z3_ast_vector v; /*in*/ + Z3_context t; /*in*/ + Z3_ast_vector _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_s, &s, _ctx); + camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); + camlidl_ml2c_z3_Z3_context(_v_t, &t, _ctx); + _res = Z3_ast_vector_translate(s, v, t); + _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(s); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_vector_to_string( + value _v_c, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast_vector v; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_vector(_v_v, &v, _ctx); + _res = Z3_ast_vector_to_string(c, v); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_ast_map( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_ast_map _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_ast_map(c); + _vres = camlidl_c2ml_z3_Z3_ast_map(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_map_contains( + value _v_c, + value _v_m, + value _v_k) +{ + Z3_context c; /*in*/ + Z3_ast_map m; /*in*/ + Z3_ast k; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_k, &k, _ctx); + _res = Z3_ast_map_contains(c, m, k); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_map_find( + value _v_c, + value _v_m, + value _v_k) +{ + Z3_context c; /*in*/ + Z3_ast_map m; /*in*/ + Z3_ast k; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_k, &k, _ctx); + _res = Z3_ast_map_find(c, m, k); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_map_insert( + value _v_c, + value _v_m, + value _v_k, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast_map m; /*in*/ + Z3_ast k; /*in*/ + Z3_ast v; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_k, &k, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_v, &v, _ctx); + Z3_ast_map_insert(c, m, k, v); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_ast_map_erase( + value _v_c, + value _v_m, + value _v_k) +{ + Z3_context c; /*in*/ + Z3_ast_map m; /*in*/ + Z3_ast k; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_k, &k, _ctx); + Z3_ast_map_erase(c, m, k); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_ast_map_reset( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_ast_map m; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); + Z3_ast_map_reset(c, m); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_ast_map_size( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_ast_map m; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); + _res = Z3_ast_map_size(c, m); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_map_keys( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_ast_map m; /*in*/ + Z3_ast_vector _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); + _res = Z3_ast_map_keys(c, m); + _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_ast_map_to_string( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_ast_map m; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_ast_map(_v_m, &m, _ctx); + _res = Z3_ast_map_to_string(c, m); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_goal( + value _v_c, + value _v_models, + value _v_unsat_cores, + value _v_proofs) +{ + Z3_context c; /*in*/ + int models; /*in*/ + int unsat_cores; /*in*/ + int proofs; /*in*/ + Z3_goal _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + models = Int_val(_v_models); + unsat_cores = Int_val(_v_unsat_cores); + proofs = Int_val(_v_proofs); + _res = Z3_mk_goal(c, models, unsat_cores, proofs); + _vres = camlidl_c2ml_z3_Z3_goal(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_precision( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + Z3_goal_prec _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_goal_precision(c, g); + _vres = camlidl_c2ml_z3_Z3_goal_prec(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_assert( + value _v_c, + value _v_g, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + Z3_ast a; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + Z3_goal_assert(c, g, a); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_goal_inconsistent( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_goal_inconsistent(c, g); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_depth( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_goal_depth(c, g); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_reset( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + Z3_goal_reset(c, g); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_goal_size( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_goal_size(c, g); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_formula( + value _v_c, + value _v_g, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + unsigned int idx; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + idx = Int_val(_v_idx); + _res = Z3_goal_formula(c, g, idx); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_num_exprs( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_goal_num_exprs(c, g); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_is_decided_sat( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_goal_is_decided_sat(c, g); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_is_decided_unsat( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_goal_is_decided_unsat(c, g); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_translate( + value _v_source, + value _v_g, + value _v_target) +{ + Z3_context source; /*in*/ + Z3_goal g; /*in*/ + Z3_context target; /*in*/ + Z3_goal _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_source, &source, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + camlidl_ml2c_z3_Z3_context(_v_target, &target, _ctx); + _res = Z3_goal_translate(source, g, target); + _vres = camlidl_c2ml_z3_Z3_goal(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(source); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_goal_to_string( + value _v_c, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_goal g; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_goal_to_string(c, g); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_tactic( + value _v_c, + value _v_name) +{ + Z3_context c; /*in*/ + Z3_string name; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); + _res = Z3_mk_tactic(c, name); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_probe( + value _v_c, + value _v_name) +{ + Z3_context c; /*in*/ + Z3_string name; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); + _res = Z3_mk_probe(c, name); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_and_then( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_tactic t1; /*in*/ + Z3_tactic t2; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t2, &t2, _ctx); + _res = Z3_tactic_and_then(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_or_else( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_tactic t1; /*in*/ + Z3_tactic t2; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t2, &t2, _ctx); + _res = Z3_tactic_or_else(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_par_or( + value _v_c, + value _v_ts) +{ + Z3_context c; /*in*/ + unsigned int num; /*in*/ + Z3_tactic const *ts; /*in*/ + Z3_tactic _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_ts); + ts = camlidl_malloc(_c1 * sizeof(Z3_tactic const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_ts, _c2); + camlidl_ml2c_z3_Z3_tactic(_v3, &ts[_c2], _ctx); + } + num = _c1; + _res = Z3_tactic_par_or(c, num, ts); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_par_and_then( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_tactic t1; /*in*/ + Z3_tactic t2; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t2, &t2, _ctx); + _res = Z3_tactic_par_and_then(c, t1, t2); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_try_for( + value _v_c, + value _v_t, + value _v_ms) +{ + Z3_context c; /*in*/ + Z3_tactic t; /*in*/ + unsigned int ms; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + ms = Int_val(_v_ms); + _res = Z3_tactic_try_for(c, t, ms); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_when( + value _v_c, + value _v_p, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_probe p; /*in*/ + Z3_tactic t; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + _res = Z3_tactic_when(c, p, t); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_cond( + value _v_c, + value _v_p, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_probe p; /*in*/ + Z3_tactic t1; /*in*/ + Z3_tactic t2; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t1, &t1, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t2, &t2, _ctx); + _res = Z3_tactic_cond(c, p, t1, t2); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_repeat( + value _v_c, + value _v_t, + value _v_max) +{ + Z3_context c; /*in*/ + Z3_tactic t; /*in*/ + unsigned int max; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + max = Int_val(_v_max); + _res = Z3_tactic_repeat(c, t, max); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_skip( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_tactic_skip(c); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_fail( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_tactic_fail(c); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_fail_if( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_probe p; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); + _res = Z3_tactic_fail_if(c, p); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_fail_if_not_decided( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_tactic_fail_if_not_decided(c); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_using_params( + value _v_c, + value _v_t, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_tactic t; /*in*/ + Z3_params p; /*in*/ + Z3_tactic _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + _res = Z3_tactic_using_params(c, t, p); + _vres = camlidl_c2ml_z3_Z3_tactic(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_const( + value _v_x, + value _v_val) +{ + Z3_context x; /*in*/ + double val; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + val = Double_val(_v_val); + _res = Z3_probe_const(x, val); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_lt( + value _v_x, + value _v_p1, + value _v_p2) +{ + Z3_context x; /*in*/ + Z3_probe p1; /*in*/ + Z3_probe p2; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); + _res = Z3_probe_lt(x, p1, p2); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_gt( + value _v_x, + value _v_p1, + value _v_p2) +{ + Z3_context x; /*in*/ + Z3_probe p1; /*in*/ + Z3_probe p2; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); + _res = Z3_probe_gt(x, p1, p2); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_le( + value _v_x, + value _v_p1, + value _v_p2) +{ + Z3_context x; /*in*/ + Z3_probe p1; /*in*/ + Z3_probe p2; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); + _res = Z3_probe_le(x, p1, p2); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_ge( + value _v_x, + value _v_p1, + value _v_p2) +{ + Z3_context x; /*in*/ + Z3_probe p1; /*in*/ + Z3_probe p2; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); + _res = Z3_probe_ge(x, p1, p2); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_eq( + value _v_x, + value _v_p1, + value _v_p2) +{ + Z3_context x; /*in*/ + Z3_probe p1; /*in*/ + Z3_probe p2; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); + _res = Z3_probe_eq(x, p1, p2); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_and( + value _v_x, + value _v_p1, + value _v_p2) +{ + Z3_context x; /*in*/ + Z3_probe p1; /*in*/ + Z3_probe p2; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); + _res = Z3_probe_and(x, p1, p2); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_or( + value _v_x, + value _v_p1, + value _v_p2) +{ + Z3_context x; /*in*/ + Z3_probe p1; /*in*/ + Z3_probe p2; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p1, &p1, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p2, &p2, _ctx); + _res = Z3_probe_or(x, p1, p2); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_not( + value _v_x, + value _v_p) +{ + Z3_context x; /*in*/ + Z3_probe p; /*in*/ + Z3_probe _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_x, &x, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); + _res = Z3_probe_not(x, p); + _vres = camlidl_c2ml_z3_Z3_probe(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(x); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_num_tactics( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_num_tactics(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_tactic_name( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_tactic_name(c, i); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_num_probes( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_num_probes(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_probe_name( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_probe_name(c, i); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_get_help( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_tactic t; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + _res = Z3_tactic_get_help(c, t); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_get_param_descrs( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_tactic t; /*in*/ + Z3_param_descrs _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + _res = Z3_tactic_get_param_descrs(c, t); + _vres = camlidl_c2ml_z3_Z3_param_descrs(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_get_descr( + value _v_c, + value _v_name) +{ + Z3_context c; /*in*/ + Z3_string name; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); + _res = Z3_tactic_get_descr(c, name); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_get_descr( + value _v_c, + value _v_name) +{ + Z3_context c; /*in*/ + Z3_string name; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_string(_v_name, &name, _ctx); + _res = Z3_probe_get_descr(c, name); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_probe_apply( + value _v_c, + value _v_p, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_probe p; /*in*/ + Z3_goal g; /*in*/ + double _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_probe(_v_p, &p, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_probe_apply(c, p, g); + _vres = copy_double(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_apply( + value _v_c, + value _v_t, + value _v_g) +{ + Z3_context c; /*in*/ + Z3_tactic t; /*in*/ + Z3_goal g; /*in*/ + Z3_apply_result _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + _res = Z3_tactic_apply(c, t, g); + _vres = camlidl_c2ml_z3_Z3_apply_result(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_tactic_apply_ex( + value _v_c, + value _v_t, + value _v_g, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_tactic t; /*in*/ + Z3_goal g; /*in*/ + Z3_params p; /*in*/ + Z3_apply_result _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + camlidl_ml2c_z3_Z3_goal(_v_g, &g, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + _res = Z3_tactic_apply_ex(c, t, g, p); + _vres = camlidl_c2ml_z3_Z3_apply_result(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_apply_result_to_string( + value _v_c, + value _v_r) +{ + Z3_context c; /*in*/ + Z3_apply_result r; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_apply_result(_v_r, &r, _ctx); + _res = Z3_apply_result_to_string(c, r); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_apply_result_get_num_subgoals( + value _v_c, + value _v_r) +{ + Z3_context c; /*in*/ + Z3_apply_result r; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_apply_result(_v_r, &r, _ctx); + _res = Z3_apply_result_get_num_subgoals(c, r); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_apply_result_get_subgoal( + value _v_c, + value _v_r, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_apply_result r; /*in*/ + unsigned int i; /*in*/ + Z3_goal _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_apply_result(_v_r, &r, _ctx); + i = Int_val(_v_i); + _res = Z3_apply_result_get_subgoal(c, r, i); + _vres = camlidl_c2ml_z3_Z3_goal(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_apply_result_convert_model( + value _v_c, + value _v_r, + value _v_i, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_apply_result r; /*in*/ + unsigned int i; /*in*/ + Z3_model m; /*in*/ + Z3_model _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_apply_result(_v_r, &r, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3_Z3_model(_v_m, &m, _ctx); + _res = Z3_apply_result_convert_model(c, r, i, m); + _vres = camlidl_c2ml_z3_Z3_model(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_solver( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_solver _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_solver(c); + _vres = camlidl_c2ml_z3_Z3_solver(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_simple_solver( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_solver _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_simple_solver(c); + _vres = camlidl_c2ml_z3_Z3_solver(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_solver_for_logic( + value _v_c, + value _v_logic) +{ + Z3_context c; /*in*/ + Z3_symbol logic; /*in*/ + Z3_solver _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_symbol(_v_logic, &logic, _ctx); + _res = Z3_mk_solver_for_logic(c, logic); + _vres = camlidl_c2ml_z3_Z3_solver(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_mk_solver_from_tactic( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_tactic t; /*in*/ + Z3_solver _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_tactic(_v_t, &t, _ctx); + _res = Z3_mk_solver_from_tactic(c, t); + _vres = camlidl_c2ml_z3_Z3_solver(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_get_help( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_help(c, s); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_get_param_descrs( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_param_descrs _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_param_descrs(c, s); + _vres = camlidl_c2ml_z3_Z3_param_descrs(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_set_params( + value _v_c, + value _v_s, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_params p; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + camlidl_ml2c_z3_Z3_params(_v_p, &p, _ctx); + Z3_solver_set_params(c, s, p); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_solver_push( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + Z3_solver_push(c, s); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_solver_pop( + value _v_c, + value _v_s, + value _v_n) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + unsigned int n; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + n = Int_val(_v_n); + Z3_solver_pop(c, s, n); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_solver_reset( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + Z3_solver_reset(c, s); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_solver_get_num_scopes( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_num_scopes(c, s); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_assert( + value _v_c, + value _v_s, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_ast a; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + camlidl_ml2c_z3_Z3_ast(_v_a, &a, _ctx); + Z3_solver_assert(c, s, a); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return Val_unit; +} + +value camlidl_z3_Z3_solver_get_assertions( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_ast_vector _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_assertions(c, s); + _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_check( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_lbool _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_check(c, s); + _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_check_assumptions( + value _v_c, + value _v_s, + value _v_assumptions) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + unsigned int num_assumptions; /*in*/ + Z3_ast const *assumptions; /*in*/ + Z3_lbool _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _c1 = Wosize_val(_v_assumptions); + assumptions = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_assumptions, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &assumptions[_c2], _ctx); + } + num_assumptions = _c1; + _res = Z3_solver_check_assumptions(c, s, num_assumptions, assumptions); + _vres = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_get_model( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_model _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_model(c, s); + _vres = camlidl_c2ml_z3_Z3_model(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_get_proof( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_proof(c, s); + _vres = camlidl_c2ml_z3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_get_unsat_core( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_ast_vector _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_unsat_core(c, s); + _vres = camlidl_c2ml_z3_Z3_ast_vector(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_get_reason_unknown( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_reason_unknown(c, s); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_get_statistics( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_stats _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_get_statistics(c, s); + _vres = camlidl_c2ml_z3_Z3_stats(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_solver_to_string( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _res = Z3_solver_to_string(c, s); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_stats_to_string( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_stats s; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); + _res = Z3_stats_to_string(c, s); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_stats_size( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_stats s; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); + _res = Z3_stats_size(c, s); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_stats_get_key( + value _v_c, + value _v_s, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_stats s; /*in*/ + unsigned int idx; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); + idx = Int_val(_v_idx); + _res = Z3_stats_get_key(c, s, idx); + _vres = camlidl_c2ml_z3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_stats_is_uint( + value _v_c, + value _v_s, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_stats s; /*in*/ + unsigned int idx; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); + idx = Int_val(_v_idx); + _res = Z3_stats_is_uint(c, s, idx); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_stats_is_double( + value _v_c, + value _v_s, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_stats s; /*in*/ + unsigned int idx; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); + idx = Int_val(_v_idx); + _res = Z3_stats_is_double(c, s, idx); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_stats_get_uint_value( + value _v_c, + value _v_s, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_stats s; /*in*/ + unsigned int idx; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); + idx = Int_val(_v_idx); + _res = Z3_stats_get_uint_value(c, s, idx); + _vres = Val_int(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_stats_get_double_value( + value _v_c, + value _v_s, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_stats s; /*in*/ + unsigned int idx; /*in*/ + double _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_stats(_v_s, &s, _ctx); + idx = Int_val(_v_idx); + _res = Z3_stats_get_double_value(c, s, idx); + _vres = copy_double(_res); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3_Z3_get_implied_equalities( + value _v_c, + value _v_s, + value _v_terms) +{ + Z3_context c; /*in*/ + Z3_solver s; /*in*/ + unsigned int num_terms; /*in*/ + Z3_ast const *terms; /*in*/ + unsigned int *class_ids; /*out*/ + Z3_lbool _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + value _v5; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3_Z3_solver(_v_s, &s, _ctx); + _c1 = Wosize_val(_v_terms); + terms = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_terms, _c2); + camlidl_ml2c_z3_Z3_ast(_v3, &terms[_c2], _ctx); + } + num_terms = _c1; + class_ids = camlidl_malloc(num_terms * sizeof(unsigned int ), _ctx); + _res = Z3_get_implied_equalities(c, s, num_terms, terms, class_ids); + Begin_roots_block(_vres, 2) + _vres[0] = camlidl_c2ml_z3_Z3_lbool(&_res, _ctx); + _vres[1] = camlidl_alloc(num_terms, 0); + for (_c4 = 0; _c4 < num_terms; _c4++) { + _v5 = Val_int(class_ids[_c4]); + modify(&Field(_vres[1], _c4), _v5); + } + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +check_error_code(c); + /* end user-supplied deallocation sequence */ + return _vresult; +} + +void camlidl_ml2c_z3V3_Z3_symbol(value _v1, Z3_symbol * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_symbol *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_symbol(Z3_symbol * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_symbol) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_symbol *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_literals(value _v1, Z3_literals * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_literals *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_literals(Z3_literals * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_literals) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_literals *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_theory(value _v1, Z3_theory * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_theory *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_theory(Z3_theory * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_theory) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_theory *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_config(value _v1, Z3_config * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_config *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_config(Z3_config * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_config) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_config *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_context(value _v1, Z3_context * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_context *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_context(Z3_context * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_context) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_context *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_sort(value _v1, Z3_sort * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_sort *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_sort(Z3_sort * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_sort) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_sort *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_func_decl(value _v1, Z3_func_decl * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_func_decl *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_func_decl(Z3_func_decl * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_func_decl) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_func_decl *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_ast(value _v1, Z3_ast * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_ast *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_ast(Z3_ast * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_ast) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_ast *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_app(value _v1, Z3_app * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_app *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_app(Z3_app * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_app) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_app *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_pattern(value _v1, Z3_pattern * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_pattern *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_pattern(Z3_pattern * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_pattern) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_pattern *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_model(value _v1, Z3_model * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_model *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_model(Z3_model * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_model) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_model *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_constructor(value _v1, Z3_constructor * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_constructor *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_constructor(Z3_constructor * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_constructor) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_constructor *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_constructor_list(value _v1, Z3_constructor_list * _c2, camlidl_ctx _ctx) +{ + *_c2 = *((Z3_constructor_list *) Bp_val(_v1)); +} + +value camlidl_c2ml_z3V3_Z3_constructor_list(Z3_constructor_list * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_alloc((sizeof(Z3_constructor_list) + sizeof(value) - 1) / sizeof(value), Abstract_tag); + *((Z3_constructor_list *) Bp_val(_v1)) = *_c2; + return _v1; +} + +void camlidl_ml2c_z3V3_Z3_string(value _v1, Z3_string * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_malloc_string(_v1, _ctx); +} + +value camlidl_c2ml_z3V3_Z3_string(Z3_string * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = copy_string((*_c2)); + return _v1; +} + +int camlidl_transl_table_z3V3_enum_1[3] = { + Z3_L_FALSE, + Z3_L_UNDEF, + Z3_L_TRUE, +}; + +void camlidl_ml2c_z3V3_Z3_lbool(value _v1, Z3_lbool * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_1[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_lbool(Z3_lbool * _c2, camlidl_ctx _ctx) +{ +value _v1; + switch((*_c2)) { + case Z3_L_FALSE: _v1 = Val_int(0); break; + case Z3_L_UNDEF: _v1 = Val_int(1); break; + case Z3_L_TRUE: _v1 = Val_int(2); break; + default: invalid_argument("typedef Z3_lbool: bad enum value"); + } + return _v1; +} + +int camlidl_transl_table_z3V3_enum_2[2] = { + Z3_INT_SYMBOL, + Z3_STRING_SYMBOL, +}; + +void camlidl_ml2c_z3V3_Z3_symbol_kind(value _v1, Z3_symbol_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_2[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_symbol_kind(Z3_symbol_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + switch((*_c2)) { + case Z3_INT_SYMBOL: _v1 = Val_int(0); break; + case Z3_STRING_SYMBOL: _v1 = Val_int(1); break; + default: invalid_argument("typedef Z3_symbol_kind: bad enum value"); + } + return _v1; +} + +int camlidl_transl_table_z3V3_enum_3[7] = { + Z3_PARAMETER_INT, + Z3_PARAMETER_DOUBLE, + Z3_PARAMETER_RATIONAL, + Z3_PARAMETER_SYMBOL, + Z3_PARAMETER_SORT, + Z3_PARAMETER_AST, + Z3_PARAMETER_FUNC_DECL, +}; + +void camlidl_ml2c_z3V3_Z3_parameter_kind(value _v1, Z3_parameter_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_3[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_parameter_kind(Z3_parameter_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_3, 7, "typedef Z3_parameter_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3V3_enum_4[10] = { + Z3_UNINTERPRETED_SORT, + Z3_BOOL_SORT, + Z3_INT_SORT, + Z3_REAL_SORT, + Z3_BV_SORT, + Z3_ARRAY_SORT, + Z3_DATATYPE_SORT, + Z3_RELATION_SORT, + Z3_FINITE_DOMAIN_SORT, + Z3_UNKNOWN_SORT, +}; + +void camlidl_ml2c_z3V3_Z3_sort_kind(value _v1, Z3_sort_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_4[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_sort_kind(Z3_sort_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_4, 10, "typedef Z3_sort_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3V3_enum_5[7] = { + Z3_NUMERAL_AST, + Z3_APP_AST, + Z3_VAR_AST, + Z3_QUANTIFIER_AST, + Z3_SORT_AST, + Z3_FUNC_DECL_AST, + Z3_UNKNOWN_AST, +}; + +void camlidl_ml2c_z3V3_Z3_ast_kind(value _v1, Z3_ast_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_5[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_ast_kind(Z3_ast_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_5, 7, "typedef Z3_ast_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3V3_enum_6[152] = { + Z3_OP_TRUE, + Z3_OP_FALSE, + Z3_OP_EQ, + Z3_OP_DISTINCT, + Z3_OP_ITE, + Z3_OP_AND, + Z3_OP_OR, + Z3_OP_IFF, + Z3_OP_XOR, + Z3_OP_NOT, + Z3_OP_IMPLIES, + Z3_OP_OEQ, + Z3_OP_ANUM, + Z3_OP_AGNUM, + Z3_OP_LE, + Z3_OP_GE, + Z3_OP_LT, + Z3_OP_GT, + Z3_OP_ADD, + Z3_OP_SUB, + Z3_OP_UMINUS, + Z3_OP_MUL, + Z3_OP_DIV, + Z3_OP_IDIV, + Z3_OP_REM, + Z3_OP_MOD, + Z3_OP_TO_REAL, + Z3_OP_TO_INT, + Z3_OP_IS_INT, + Z3_OP_POWER, + Z3_OP_STORE, + Z3_OP_SELECT, + Z3_OP_CONST_ARRAY, + Z3_OP_ARRAY_MAP, + Z3_OP_ARRAY_DEFAULT, + Z3_OP_SET_UNION, + Z3_OP_SET_INTERSECT, + Z3_OP_SET_DIFFERENCE, + Z3_OP_SET_COMPLEMENT, + Z3_OP_SET_SUBSET, + Z3_OP_AS_ARRAY, + Z3_OP_BNUM, + Z3_OP_BIT1, + Z3_OP_BIT0, + Z3_OP_BNEG, + Z3_OP_BADD, + Z3_OP_BSUB, + Z3_OP_BMUL, + Z3_OP_BSDIV, + Z3_OP_BUDIV, + Z3_OP_BSREM, + Z3_OP_BUREM, + Z3_OP_BSMOD, + Z3_OP_BSDIV0, + Z3_OP_BUDIV0, + Z3_OP_BSREM0, + Z3_OP_BUREM0, + Z3_OP_BSMOD0, + Z3_OP_ULEQ, + Z3_OP_SLEQ, + Z3_OP_UGEQ, + Z3_OP_SGEQ, + Z3_OP_ULT, + Z3_OP_SLT, + Z3_OP_UGT, + Z3_OP_SGT, + Z3_OP_BAND, + Z3_OP_BOR, + Z3_OP_BNOT, + Z3_OP_BXOR, + Z3_OP_BNAND, + Z3_OP_BNOR, + Z3_OP_BXNOR, + Z3_OP_CONCAT, + Z3_OP_SIGN_EXT, + Z3_OP_ZERO_EXT, + Z3_OP_EXTRACT, + Z3_OP_REPEAT, + Z3_OP_BREDOR, + Z3_OP_BREDAND, + Z3_OP_BCOMP, + Z3_OP_BSHL, + Z3_OP_BLSHR, + Z3_OP_BASHR, + Z3_OP_ROTATE_LEFT, + Z3_OP_ROTATE_RIGHT, + Z3_OP_EXT_ROTATE_LEFT, + Z3_OP_EXT_ROTATE_RIGHT, + Z3_OP_INT2BV, + Z3_OP_BV2INT, + Z3_OP_CARRY, + Z3_OP_XOR3, + Z3_OP_PR_UNDEF, + Z3_OP_PR_TRUE, + Z3_OP_PR_ASSERTED, + Z3_OP_PR_GOAL, + Z3_OP_PR_MODUS_PONENS, + Z3_OP_PR_REFLEXIVITY, + Z3_OP_PR_SYMMETRY, + Z3_OP_PR_TRANSITIVITY, + Z3_OP_PR_TRANSITIVITY_STAR, + Z3_OP_PR_MONOTONICITY, + Z3_OP_PR_QUANT_INTRO, + Z3_OP_PR_DISTRIBUTIVITY, + Z3_OP_PR_AND_ELIM, + Z3_OP_PR_NOT_OR_ELIM, + Z3_OP_PR_REWRITE, + Z3_OP_PR_REWRITE_STAR, + Z3_OP_PR_PULL_QUANT, + Z3_OP_PR_PULL_QUANT_STAR, + Z3_OP_PR_PUSH_QUANT, + Z3_OP_PR_ELIM_UNUSED_VARS, + Z3_OP_PR_DER, + Z3_OP_PR_QUANT_INST, + Z3_OP_PR_HYPOTHESIS, + Z3_OP_PR_LEMMA, + Z3_OP_PR_UNIT_RESOLUTION, + Z3_OP_PR_IFF_TRUE, + Z3_OP_PR_IFF_FALSE, + Z3_OP_PR_COMMUTATIVITY, + Z3_OP_PR_DEF_AXIOM, + Z3_OP_PR_DEF_INTRO, + Z3_OP_PR_APPLY_DEF, + Z3_OP_PR_IFF_OEQ, + Z3_OP_PR_NNF_POS, + Z3_OP_PR_NNF_NEG, + Z3_OP_PR_NNF_STAR, + Z3_OP_PR_CNF_STAR, + Z3_OP_PR_SKOLEMIZE, + Z3_OP_PR_MODUS_PONENS_OEQ, + Z3_OP_PR_TH_LEMMA, + Z3_OP_PR_HYPER_RESOLVE, + Z3_OP_RA_STORE, + Z3_OP_RA_EMPTY, + Z3_OP_RA_IS_EMPTY, + Z3_OP_RA_JOIN, + Z3_OP_RA_UNION, + Z3_OP_RA_WIDEN, + Z3_OP_RA_PROJECT, + Z3_OP_RA_FILTER, + Z3_OP_RA_NEGATION_FILTER, + Z3_OP_RA_RENAME, + Z3_OP_RA_COMPLEMENT, + Z3_OP_RA_SELECT, + Z3_OP_RA_CLONE, + Z3_OP_FD_LT, + Z3_OP_LABEL, + Z3_OP_LABEL_LIT, + Z3_OP_DT_CONSTRUCTOR, + Z3_OP_DT_RECOGNISER, + Z3_OP_DT_ACCESSOR, + Z3_OP_UNINTERPRETED, +}; + +void camlidl_ml2c_z3V3_Z3_decl_kind(value _v1, Z3_decl_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_6[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_decl_kind(Z3_decl_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_6, 152, "typedef Z3_decl_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3V3_enum_7[7] = { + Z3_PK_UINT, + Z3_PK_BOOL, + Z3_PK_DOUBLE, + Z3_PK_SYMBOL, + Z3_PK_STRING, + Z3_PK_OTHER, + Z3_PK_INVALID, +}; + +void camlidl_ml2c_z3V3_Z3_param_kind(value _v1, Z3_param_kind * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_7[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_param_kind(Z3_param_kind * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_7, 7, "typedef Z3_param_kind: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3V3_enum_8[8] = { + Z3_NO_FAILURE, + Z3_UNKNOWN, + Z3_TIMEOUT, + Z3_MEMOUT_WATERMARK, + Z3_CANCELED, + Z3_NUM_CONFLICTS, + Z3_THEORY, + Z3_QUANTIFIERS, +}; + +void camlidl_ml2c_z3V3_Z3_search_failure(value _v1, Z3_search_failure * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_8[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_search_failure(Z3_search_failure * _c2, camlidl_ctx _ctx) +{ +value _v1; + _v1 = camlidl_find_enum((*_c2), camlidl_transl_table_z3V3_enum_8, 8, "typedef Z3_search_failure: bad enum value"); + return _v1; +} + +int camlidl_transl_table_z3V3_enum_9[4] = { + Z3_PRINT_SMTLIB_FULL, + Z3_PRINT_LOW_LEVEL, + Z3_PRINT_SMTLIB_COMPLIANT, + Z3_PRINT_SMTLIB2_COMPLIANT, +}; + +void camlidl_ml2c_z3V3_Z3_ast_print_mode(value _v1, Z3_ast_print_mode * _c2, camlidl_ctx _ctx) +{ + (*_c2) = camlidl_transl_table_z3V3_enum_9[Int_val(_v1)]; +} + +value camlidl_c2ml_z3V3_Z3_ast_print_mode(Z3_ast_print_mode * _c2, camlidl_ctx _ctx) +{ +value _v1; + switch((*_c2)) { + case Z3_PRINT_SMTLIB_FULL: _v1 = Val_int(0); break; + case Z3_PRINT_LOW_LEVEL: _v1 = Val_int(1); break; + case Z3_PRINT_SMTLIB_COMPLIANT: _v1 = Val_int(2); break; + case Z3_PRINT_SMTLIB2_COMPLIANT: _v1 = Val_int(3); break; + default: invalid_argument("typedef Z3_ast_print_mode: bad enum value"); + } + return _v1; +} + +value camlidl_z3V3_Z3_mk_config(value _unit) +{ + Z3_config _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + _res = Z3_mk_config(); + _vres = camlidl_c2ml_z3V3_Z3_config(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_del_config( + value _v_c) +{ + Z3_config c; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_config(_v_c, &c, _ctx); + Z3_del_config(c); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_set_param_value( + value _v_c, + value _v_param_id, + value _v_param_value) +{ + Z3_config c; /*in*/ + Z3_string param_id; /*in*/ + Z3_string param_value; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_config(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_param_id, ¶m_id, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_param_value, ¶m_value, _ctx); + Z3_set_param_value(c, param_id, param_value); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_mk_context( + value _v_c) +{ + Z3_config c; /*in*/ + Z3_context _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_config(_v_c, &c, _ctx); + _res = Z3_mk_context(c); + _vres = camlidl_c2ml_z3V3_Z3_context(&_res, _ctx); + camlidl_free(_ctx); + /* begin user-supplied deallocation sequence */ +Z3_set_error_handler(_res, caml_z3_error_handler); + /* end user-supplied deallocation sequence */ + return _vres; +} + +value camlidl_z3V3_Z3_del_context( + value _v_c) +{ + Z3_context c; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + Z3_del_context(c); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_update_param_value( + value _v_c, + value _v_param_id, + value _v_param_value) +{ + Z3_context c; /*in*/ + Z3_string param_id; /*in*/ + Z3_string param_value; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_param_id, ¶m_id, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_param_value, ¶m_value, _ctx); + Z3_update_param_value(c, param_id, param_value); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_get_param_value( + value _v_c, + value _v_param_id) +{ + Z3_context c; /*in*/ + Z3_string param_id; /*in*/ + Z3_string *param_value; /*out*/ + Z3_string _c1; + value _v2; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_param_id, ¶m_id, _ctx); + param_value = &_c1; + Z3_get_param_value(c, param_id, param_value); + if (param_value == NULL) { + _vres = Val_int(0); + } else { + _v2 = camlidl_c2ml_z3V3_Z3_string(&*param_value, _ctx); + Begin_root(_v2) + _vres = camlidl_alloc_small(1, 0); + Field(_vres, 0) = _v2; + End_roots(); + } + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_int_symbol( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + int i; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_mk_int_symbol(c, i); + _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_string_symbol( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_string s; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_s, &s, _ctx); + _res = Z3_mk_string_symbol(c, s); + _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_uninterpreted_sort( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_mk_uninterpreted_sort(c, s); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bool_sort( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_bool_sort(c); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_int_sort( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_int_sort(c); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_real_sort( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_real_sort(c); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bv_sort( + value _v_c, + value _v_sz) +{ + Z3_context c; /*in*/ + unsigned int sz; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + sz = Int_val(_v_sz); + _res = Z3_mk_bv_sort(c, sz); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_finite_domain_sort( + value _v_c, + value _v_name, + value _v_size) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + __int64 size; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); + size = Int64_val(_v_size); + _res = Z3_mk_finite_domain_sort(c, name, size); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_array_sort( + value _v_c, + value _v_domain, + value _v_range) +{ + Z3_context c; /*in*/ + Z3_sort domain; /*in*/ + Z3_sort range; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_domain, &domain, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); + _res = Z3_mk_array_sort(c, domain, range); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_tuple_sort( + value _v_c, + value _v_mk_tuple_name, + value _v_field_names, + value _v_field_sorts) +{ + Z3_context c; /*in*/ + Z3_symbol mk_tuple_name; /*in*/ + unsigned int num_fields; /*in*/ + Z3_symbol const *field_names; /*in*/ + Z3_sort const *field_sorts; /*in*/ + Z3_func_decl *mk_tuple_decl; /*out*/ + Z3_func_decl *proj_decl; /*out*/ + Z3_sort _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + Z3_func_decl _c7; + mlsize_t _c8; + value _v9; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_mk_tuple_name, &mk_tuple_name, _ctx); + _c1 = Wosize_val(_v_field_names); + field_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_field_names, _c2); + camlidl_ml2c_z3V3_Z3_symbol(_v3, &field_names[_c2], _ctx); + } + num_fields = _c1; + _c4 = Wosize_val(_v_field_sorts); + field_sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_field_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &field_sorts[_c5], _ctx); + } + num_fields = _c4; + mk_tuple_decl = &_c7; + proj_decl = camlidl_malloc(num_fields * sizeof(Z3_func_decl ), _ctx); + _res = Z3_mk_tuple_sort(c, mk_tuple_name, num_fields, field_names, field_sorts, mk_tuple_decl, proj_decl); + Begin_roots_block(_vres, 3) + _vres[0] = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + _vres[1] = camlidl_c2ml_z3V3_Z3_func_decl(&*mk_tuple_decl, _ctx); + _vres[2] = camlidl_alloc(num_fields, 0); + Begin_root(_vres[2]) + for (_c8 = 0; _c8 < num_fields; _c8++) { + _v9 = camlidl_c2ml_z3V3_Z3_func_decl(&proj_decl[_c8], _ctx); + modify(&Field(_vres[2], _c8), _v9); + } + End_roots() + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_mk_enumeration_sort( + value _v_c, + value _v_name, + value _v_enum_names) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + unsigned int n; /*in*/ + Z3_symbol const *enum_names; /*in*/ + Z3_func_decl *enum_consts; /*out*/ + Z3_func_decl *enum_testers; /*out*/ + Z3_sort _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + value _v5; + mlsize_t _c6; + value _v7; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); + _c1 = Wosize_val(_v_enum_names); + enum_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_enum_names, _c2); + camlidl_ml2c_z3V3_Z3_symbol(_v3, &enum_names[_c2], _ctx); + } + n = _c1; + enum_consts = camlidl_malloc(n * sizeof(Z3_func_decl ), _ctx); + enum_testers = camlidl_malloc(n * sizeof(Z3_func_decl ), _ctx); + _res = Z3_mk_enumeration_sort(c, name, n, enum_names, enum_consts, enum_testers); + Begin_roots_block(_vres, 3) + _vres[0] = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + _vres[1] = camlidl_alloc(n, 0); + Begin_root(_vres[1]) + for (_c4 = 0; _c4 < n; _c4++) { + _v5 = camlidl_c2ml_z3V3_Z3_func_decl(&enum_consts[_c4], _ctx); + modify(&Field(_vres[1], _c4), _v5); + } + End_roots() + _vres[2] = camlidl_alloc(n, 0); + Begin_root(_vres[2]) + for (_c6 = 0; _c6 < n; _c6++) { + _v7 = camlidl_c2ml_z3V3_Z3_func_decl(&enum_testers[_c6], _ctx); + modify(&Field(_vres[2], _c6), _v7); + } + End_roots() + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_mk_list_sort( + value _v_c, + value _v_name, + value _v_elem_sort) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + Z3_sort elem_sort; /*in*/ + 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; /*out*/ + Z3_sort _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + Z3_func_decl _c1; + Z3_func_decl _c2; + Z3_func_decl _c3; + Z3_func_decl _c4; + Z3_func_decl _c5; + Z3_func_decl _c6; + value _vresult; + value _vres[7] = { 0, 0, 0, 0, 0, 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_elem_sort, &elem_sort, _ctx); + nil_decl = &_c1; + is_nil_decl = &_c2; + cons_decl = &_c3; + is_cons_decl = &_c4; + head_decl = &_c5; + tail_decl = &_c6; + _res = Z3_mk_list_sort(c, name, elem_sort, nil_decl, is_nil_decl, cons_decl, is_cons_decl, head_decl, tail_decl); + Begin_roots_block(_vres, 7) + _vres[0] = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + _vres[1] = camlidl_c2ml_z3V3_Z3_func_decl(&*nil_decl, _ctx); + _vres[2] = camlidl_c2ml_z3V3_Z3_func_decl(&*is_nil_decl, _ctx); + _vres[3] = camlidl_c2ml_z3V3_Z3_func_decl(&*cons_decl, _ctx); + _vres[4] = camlidl_c2ml_z3V3_Z3_func_decl(&*is_cons_decl, _ctx); + _vres[5] = camlidl_c2ml_z3V3_Z3_func_decl(&*head_decl, _ctx); + _vres[6] = camlidl_c2ml_z3V3_Z3_func_decl(&*tail_decl, _ctx); + _vresult = camlidl_alloc_small(7, 0); + { mlsize_t _c7; + for (_c7 = 0; _c7 < 7; _c7++) Field(_vresult, _c7) = _vres[_c7]; + } + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_mk_constructor( + value _v_c, + value _v_name, + value _v_recognizer, + value _v_field_names, + value _v_sorts, + value _v_sort_refs) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + Z3_symbol recognizer; /*in*/ + unsigned int num_fields; /*in*/ + Z3_symbol const *field_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int *sort_refs; /*in*/ + Z3_constructor _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_recognizer, &recognizer, _ctx); + _c1 = Wosize_val(_v_field_names); + field_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_field_names, _c2); + camlidl_ml2c_z3V3_Z3_symbol(_v3, &field_names[_c2], _ctx); + } + num_fields = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_fields = _c4; + _c7 = Wosize_val(_v_sort_refs); + sort_refs = camlidl_malloc(_c7 * sizeof(unsigned int ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_sort_refs, _c8); + sort_refs[_c8] = Int_val(_v9); + } + num_fields = _c7; + _res = Z3_mk_constructor(c, name, recognizer, num_fields, field_names, sorts, sort_refs); + _vres = camlidl_c2ml_z3V3_Z3_constructor(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_constructor_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_mk_constructor(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3V3_Z3_del_constructor( + value _v_c, + value _v_constr) +{ + Z3_context c; /*in*/ + Z3_constructor constr; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_constructor(_v_constr, &constr, _ctx); + Z3_del_constructor(c, constr); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_mk_datatype( + value _v_c, + value _v_name, + value _v_constructors) +{ + Z3_context c; /*in*/ + Z3_symbol name; /*in*/ + unsigned int num_constructors; /*in*/ + Z3_constructor *constructors; /*in,out*/ + Z3_sort _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + value _v5; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_name, &name, _ctx); + _c1 = Wosize_val(_v_constructors); + constructors = camlidl_malloc(_c1 * sizeof(Z3_constructor ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_constructors, _c2); + camlidl_ml2c_z3V3_Z3_constructor(_v3, &constructors[_c2], _ctx); + } + num_constructors = _c1; + _res = Z3_mk_datatype(c, name, num_constructors, constructors); + Begin_roots_block(_vres, 2) + _vres[0] = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + _vres[1] = camlidl_alloc(num_constructors, 0); + Begin_root(_vres[1]) + for (_c4 = 0; _c4 < num_constructors; _c4++) { + _v5 = camlidl_c2ml_z3V3_Z3_constructor(&constructors[_c4], _ctx); + modify(&Field(_vres[1], _c4), _v5); + } + End_roots() + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_mk_constructor_list( + value _v_c, + value _v_constructors) +{ + Z3_context c; /*in*/ + unsigned int num_constructors; /*in*/ + Z3_constructor const *constructors; /*in*/ + Z3_constructor_list _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_constructors); + constructors = camlidl_malloc(_c1 * sizeof(Z3_constructor const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_constructors, _c2); + camlidl_ml2c_z3V3_Z3_constructor(_v3, &constructors[_c2], _ctx); + } + num_constructors = _c1; + _res = Z3_mk_constructor_list(c, num_constructors, constructors); + _vres = camlidl_c2ml_z3V3_Z3_constructor_list(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_del_constructor_list( + value _v_c, + value _v_clist) +{ + Z3_context c; /*in*/ + Z3_constructor_list clist; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_constructor_list(_v_clist, &clist, _ctx); + Z3_del_constructor_list(c, clist); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_mk_datatypes( + value _v_c, + value _v_sort_names, + value _v_constructor_lists) +{ + Z3_context c; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort *sorts; /*out*/ + Z3_constructor_list *constructor_lists; /*in,out*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + value _v8; + mlsize_t _c9; + value _v10; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_constructor_lists); + constructor_lists = camlidl_malloc(_c4 * sizeof(Z3_constructor_list ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_constructor_lists, _c5); + camlidl_ml2c_z3V3_Z3_constructor_list(_v6, &constructor_lists[_c5], _ctx); + } + num_sorts = _c4; + sorts = camlidl_malloc(num_sorts * sizeof(Z3_sort ), _ctx); + Z3_mk_datatypes(c, num_sorts, sort_names, sorts, constructor_lists); + Begin_roots_block(_vres, 2) + _vres[0] = camlidl_alloc(num_sorts, 0); + Begin_root(_vres[0]) + for (_c7 = 0; _c7 < num_sorts; _c7++) { + _v8 = camlidl_c2ml_z3V3_Z3_sort(&sorts[_c7], _ctx); + modify(&Field(_vres[0], _c7), _v8); + } + End_roots() + _vres[1] = camlidl_alloc(num_sorts, 0); + Begin_root(_vres[1]) + for (_c9 = 0; _c9 < num_sorts; _c9++) { + _v10 = camlidl_c2ml_z3V3_Z3_constructor_list(&constructor_lists[_c9], _ctx); + modify(&Field(_vres[1], _c9), _v10); + } + End_roots() + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_query_constructor( + value _v_c, + value _v_constr, + value _v_num_fields) +{ + Z3_context c; /*in*/ + Z3_constructor constr; /*in*/ + unsigned int num_fields; /*in*/ + Z3_func_decl *constructor; /*out*/ + Z3_func_decl *tester; /*out*/ + Z3_func_decl *accessors; /*out*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + Z3_func_decl _c1; + Z3_func_decl _c2; + mlsize_t _c3; + value _v4; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_constructor(_v_constr, &constr, _ctx); + num_fields = Int_val(_v_num_fields); + constructor = &_c1; + tester = &_c2; + accessors = camlidl_malloc(num_fields * sizeof(Z3_func_decl ), _ctx); + Z3_query_constructor(c, constr, num_fields, constructor, tester, accessors); + Begin_roots_block(_vres, 3) + _vres[0] = camlidl_c2ml_z3V3_Z3_func_decl(&*constructor, _ctx); + _vres[1] = camlidl_c2ml_z3V3_Z3_func_decl(&*tester, _ctx); + _vres[2] = camlidl_alloc(num_fields, 0); + Begin_root(_vres[2]) + for (_c3 = 0; _c3 < num_fields; _c3++) { + _v4 = camlidl_c2ml_z3V3_Z3_func_decl(&accessors[_c3], _ctx); + modify(&Field(_vres[2], _c3), _v4); + } + End_roots() + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_mk_func_decl( + value _v_c, + value _v_s, + value _v_domain, + value _v_range) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + unsigned int domain_size; /*in*/ + Z3_sort const *domain; /*in*/ + Z3_sort range; /*in*/ + Z3_func_decl _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + _c1 = Wosize_val(_v_domain); + domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_domain, _c2); + camlidl_ml2c_z3V3_Z3_sort(_v3, &domain[_c2], _ctx); + } + domain_size = _c1; + camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); + _res = Z3_mk_func_decl(c, s, domain_size, domain, range); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_app( + value _v_c, + value _v_d, + value _v_args) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_app(c, d, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_const( + value _v_c, + value _v_s, + value _v_ty) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_const(c, s, ty); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_fresh_func_decl( + value _v_c, + value _v_prefix, + value _v_domain, + value _v_range) +{ + Z3_context c; /*in*/ + Z3_string prefix; /*in*/ + unsigned int domain_size; /*in*/ + Z3_sort const *domain; /*in*/ + Z3_sort range; /*in*/ + Z3_func_decl _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_prefix, &prefix, _ctx); + _c1 = Wosize_val(_v_domain); + domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_domain, _c2); + camlidl_ml2c_z3V3_Z3_sort(_v3, &domain[_c2], _ctx); + } + domain_size = _c1; + camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); + _res = Z3_mk_fresh_func_decl(c, prefix, domain_size, domain, range); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_fresh_const( + value _v_c, + value _v_prefix, + value _v_ty) +{ + Z3_context c; /*in*/ + Z3_string prefix; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_prefix, &prefix, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_fresh_const(c, prefix, ty); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_true( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_true(c); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_false( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_mk_false(c); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_eq( + value _v_c, + value _v_l, + value _v_r) +{ + Z3_context c; /*in*/ + Z3_ast l; /*in*/ + Z3_ast r; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_l, &l, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_r, &r, _ctx); + _res = Z3_mk_eq(c, l, r); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_distinct( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_distinct(c, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_not( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_mk_not(c, a); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_ite( + value _v_c, + value _v_t1, + value _v_t2, + value _v_t3) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast t3; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t3, &t3, _ctx); + _res = Z3_mk_ite(c, t1, t2, t3); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_iff( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_iff(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_implies( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_implies(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_xor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_xor(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_and( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_and(c, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_or( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_or(c, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_add( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_add(c, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_mul( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_mul(c, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_sub( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_sub(c, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_unary_minus( + value _v_c, + value _v_arg) +{ + Z3_context c; /*in*/ + Z3_ast arg; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg, &arg, _ctx); + _res = Z3_mk_unary_minus(c, arg); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_div( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_div(c, arg1, arg2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_mod( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_mod(c, arg1, arg2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_rem( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_rem(c, arg1, arg2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_power( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_power(c, arg1, arg2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_lt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_lt(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_le( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_le(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_gt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_gt(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_ge( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_ge(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_int2real( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_int2real(c, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_real2int( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_real2int(c, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_is_int( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_is_int(c, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvnot( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvnot(c, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvredand( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvredand(c, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvredor( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvredor(c, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvand( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvand(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvor(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvxor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvxor(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvnand( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvnand(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvnor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvnor(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvxnor( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvxnor(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvneg( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvneg(c, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvadd( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvadd(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsub( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsub(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvmul( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvmul(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvudiv( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvudiv(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsdiv( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsdiv(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvurem( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvurem(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsrem( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsrem(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsmod( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsmod(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvult( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvult(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvslt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvslt(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvule( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvule(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsle( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsle(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvuge( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvuge(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsge( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsge(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvugt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvugt(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsgt( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsgt(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_concat( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_concat(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_extract( + value _v_c, + value _v_high, + value _v_low, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int high; /*in*/ + unsigned int low; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + high = Int_val(_v_high); + low = Int_val(_v_low); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_extract(c, high, low, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_sign_ext( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_sign_ext(c, i, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_zero_ext( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_zero_ext(c, i, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_repeat( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_repeat(c, i, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvshl( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvshl(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvlshr( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvlshr(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvashr( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvashr(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_rotate_left( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_rotate_left(c, i, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_rotate_right( + value _v_c, + value _v_i, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_rotate_right(c, i, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_ext_rotate_left( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_ext_rotate_left(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_ext_rotate_right( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_ext_rotate_right(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_int2bv( + value _v_c, + value _v_n, + value _v_t1) +{ + Z3_context c; /*in*/ + unsigned int n; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + n = Int_val(_v_n); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_int2bv(c, n, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bv2int( + value _v_c, + value _v_t1, + value _v_is_signed) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + int is_signed; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + is_signed = Int_val(_v_is_signed); + _res = Z3_mk_bv2int(c, t1, is_signed); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvadd_no_overflow( + value _v_c, + value _v_t1, + value _v_t2, + value _v_is_signed) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + int is_signed; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + is_signed = Int_val(_v_is_signed); + _res = Z3_mk_bvadd_no_overflow(c, t1, t2, is_signed); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvadd_no_underflow( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvadd_no_underflow(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsub_no_overflow( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsub_no_overflow(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsub_no_underflow( + value _v_c, + value _v_t1, + value _v_t2, + value _v_is_signed) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + int is_signed; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + is_signed = Int_val(_v_is_signed); + _res = Z3_mk_bvsub_no_underflow(c, t1, t2, is_signed); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvsdiv_no_overflow( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvsdiv_no_overflow(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvneg_no_overflow( + value _v_c, + value _v_t1) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + _res = Z3_mk_bvneg_no_overflow(c, t1); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvmul_no_overflow( + value _v_c, + value _v_t1, + value _v_t2, + value _v_is_signed) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + int is_signed; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + is_signed = Int_val(_v_is_signed); + _res = Z3_mk_bvmul_no_overflow(c, t1, t2, is_signed); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bvmul_no_underflow( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_mk_bvmul_no_underflow(c, t1, t2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_select( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_i, &i, _ctx); + _res = Z3_mk_select(c, a, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_store( + value _v_c, + value _v_a, + value _v_i, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast i; /*in*/ + Z3_ast v; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_i, &i, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); + _res = Z3_mk_store(c, a, i, v); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_const_array( + value _v_c, + value _v_domain, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_sort domain; /*in*/ + Z3_ast v; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_domain, &domain, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); + _res = Z3_mk_const_array(c, domain, v); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_map( + value _v_c, + value _v_f, + value _v_n, + value _v_args) +{ + Z3_context c; /*in*/ + Z3_func_decl f; /*in*/ + unsigned int n; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + Z3_ast _c1; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_f, &f, _ctx); + n = Int_val(_v_n); + args = &_c1; + camlidl_ml2c_z3V3_Z3_ast(_v_args, &_c1, _ctx); + _res = Z3_mk_map(c, f, n, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_array_default( + value _v_c, + value _v_array) +{ + Z3_context c; /*in*/ + Z3_ast array; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_array, &array, _ctx); + _res = Z3_mk_array_default(c, array); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_sort( + value _v_c, + value _v_ty) +{ + Z3_context c; /*in*/ + Z3_sort ty; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_set_sort(c, ty); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_empty_set( + value _v_c, + value _v_domain) +{ + Z3_context c; /*in*/ + Z3_sort domain; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_domain, &domain, _ctx); + _res = Z3_mk_empty_set(c, domain); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_full_set( + value _v_c, + value _v_domain) +{ + Z3_context c; /*in*/ + Z3_sort domain; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_domain, &domain, _ctx); + _res = Z3_mk_full_set(c, domain); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_add( + value _v_c, + value _v_set, + value _v_elem) +{ + Z3_context c; /*in*/ + Z3_ast set; /*in*/ + Z3_ast elem; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_set, &set, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_elem, &elem, _ctx); + _res = Z3_mk_set_add(c, set, elem); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_del( + value _v_c, + value _v_set, + value _v_elem) +{ + Z3_context c; /*in*/ + Z3_ast set; /*in*/ + Z3_ast elem; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_set, &set, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_elem, &elem, _ctx); + _res = Z3_mk_set_del(c, set, elem); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_union( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_set_union(c, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_intersect( + value _v_c, + value _v_args) +{ + Z3_context c; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_mk_set_intersect(c, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_difference( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_set_difference(c, arg1, arg2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_complement( + value _v_c, + value _v_arg) +{ + Z3_context c; /*in*/ + Z3_ast arg; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg, &arg, _ctx); + _res = Z3_mk_set_complement(c, arg); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_member( + value _v_c, + value _v_elem, + value _v_set) +{ + Z3_context c; /*in*/ + Z3_ast elem; /*in*/ + Z3_ast set; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_elem, &elem, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_set, &set, _ctx); + _res = Z3_mk_set_member(c, elem, set); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_set_subset( + value _v_c, + value _v_arg1, + value _v_arg2) +{ + Z3_context c; /*in*/ + Z3_ast arg1; /*in*/ + Z3_ast arg2; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg1, &arg1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_arg2, &arg2, _ctx); + _res = Z3_mk_set_subset(c, arg1, arg2); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_numeral( + value _v_c, + value _v_numeral, + value _v_ty) +{ + Z3_context c; /*in*/ + Z3_string numeral; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_numeral, &numeral, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_numeral(c, numeral, ty); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_real( + value _v_c, + value _v_num, + value _v_den) +{ + Z3_context c; /*in*/ + int num; /*in*/ + int den; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + num = Int_val(_v_num); + den = Int_val(_v_den); + _res = Z3_mk_real(c, num, den); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_int( + value _v_c, + value _v_v, + value _v_ty) +{ + Z3_context c; /*in*/ + int v; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + v = Int_val(_v_v); + camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_int(c, v, ty); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_int64( + value _v_c, + value _v_v, + value _v_ty) +{ + Z3_context c; /*in*/ + __int64 v; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + v = Int64_val(_v_v); + camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_int64(c, v, ty); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_pattern( + value _v_c, + value _v_terms) +{ + Z3_context c; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_ast const *terms; /*in*/ + Z3_pattern _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_terms); + terms = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_terms, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &terms[_c2], _ctx); + } + num_patterns = _c1; + _res = Z3_mk_pattern(c, num_patterns, terms); + _vres = camlidl_c2ml_z3V3_Z3_pattern(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_bound( + value _v_c, + value _v_index, + value _v_ty) +{ + Z3_context c; /*in*/ + unsigned int index; /*in*/ + Z3_sort ty; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + index = Int_val(_v_index); + camlidl_ml2c_z3V3_Z3_sort(_v_ty, &ty, _ctx); + _res = Z3_mk_bound(c, index, ty); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_forall( + value _v_c, + value _v_weight, + value _v_patterns, + value _v_sorts, + value _v_decl_names, + value _v_body) +{ + Z3_context c; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_decls; /*in*/ + Z3_sort const *sorts; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_patterns, _c2); + camlidl_ml2c_z3V3_Z3_pattern(_v3, &patterns[_c2], _ctx); + } + num_patterns = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_decls = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_forall(c, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_forall_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_mk_forall(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3V3_Z3_mk_exists( + value _v_c, + value _v_weight, + value _v_patterns, + value _v_sorts, + value _v_decl_names, + value _v_body) +{ + Z3_context c; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_decls; /*in*/ + Z3_sort const *sorts; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_patterns, _c2); + camlidl_ml2c_z3V3_Z3_pattern(_v3, &patterns[_c2], _ctx); + } + num_patterns = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_decls = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_exists(c, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_exists_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_mk_exists(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3V3_Z3_mk_quantifier( + value _v_c, + value _v_is_forall, + value _v_weight, + value _v_patterns, + value _v_sorts, + value _v_decl_names, + value _v_body) +{ + Z3_context c; /*in*/ + int is_forall; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_decls; /*in*/ + Z3_sort const *sorts; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + is_forall = Int_val(_v_is_forall); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_patterns, _c2); + camlidl_ml2c_z3V3_Z3_pattern(_v3, &patterns[_c2], _ctx); + } + num_patterns = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_decls = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_quantifier(c, is_forall, weight, num_patterns, patterns, num_decls, sorts, decl_names, body); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_quantifier_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_mk_quantifier(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); +} + +value camlidl_z3V3_Z3_mk_quantifier_ex( + value _v_c, + value _v_is_forall, + value _v_weight, + value _v_quantifier_id, + value _v_skolem_id, + value _v_patterns, + value _v_no_patterns, + value _v_sorts, + value _v_decl_names, + value _v_body) +{ + Z3_context c; /*in*/ + int is_forall; /*in*/ + unsigned int weight; /*in*/ + Z3_symbol quantifier_id; /*in*/ + Z3_symbol skolem_id; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_no_patterns; /*in*/ + Z3_ast const *no_patterns; /*in*/ + unsigned int num_decls; /*in*/ + Z3_sort const *sorts; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + is_forall = Int_val(_v_is_forall); + weight = Int_val(_v_weight); + camlidl_ml2c_z3V3_Z3_symbol(_v_quantifier_id, &quantifier_id, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_skolem_id, &skolem_id, _ctx); + _c1 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c1 * sizeof(Z3_pattern const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_patterns, _c2); + camlidl_ml2c_z3V3_Z3_pattern(_v3, &patterns[_c2], _ctx); + } + num_patterns = _c1; + _c4 = Wosize_val(_v_no_patterns); + no_patterns = camlidl_malloc(_c4 * sizeof(Z3_ast const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_no_patterns, _c5); + camlidl_ml2c_z3V3_Z3_ast(_v6, &no_patterns[_c5], _ctx); + } + num_no_patterns = _c4; + _c7 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c7 * sizeof(Z3_sort const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_sorts, _c8); + camlidl_ml2c_z3V3_Z3_sort(_v9, &sorts[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c10 * sizeof(Z3_symbol const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decl_names, _c11); + camlidl_ml2c_z3V3_Z3_symbol(_v12, &decl_names[_c11], _ctx); + } + num_decls = _c10; + camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_quantifier_ex(c, is_forall, weight, quantifier_id, skolem_id, num_patterns, patterns, num_no_patterns, no_patterns, num_decls, sorts, decl_names, body); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_quantifier_ex_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_mk_quantifier_ex(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); +} + +value camlidl_z3V3_Z3_mk_forall_const( + value _v_c, + value _v_weight, + value _v_bound, + value _v_patterns, + value _v_body) +{ + Z3_context c; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_bound; /*in*/ + Z3_app const *bound; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_bound); + bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_bound, _c2); + camlidl_ml2c_z3V3_Z3_app(_v3, &bound[_c2], _ctx); + } + num_bound = _c1; + _c4 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_patterns, _c5); + camlidl_ml2c_z3V3_Z3_pattern(_v6, &patterns[_c5], _ctx); + } + num_patterns = _c4; + camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_forall_const(c, weight, num_bound, bound, num_patterns, patterns, body); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_exists_const( + value _v_c, + value _v_weight, + value _v_bound, + value _v_patterns, + value _v_body) +{ + Z3_context c; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_bound; /*in*/ + Z3_app const *bound; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_bound); + bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_bound, _c2); + camlidl_ml2c_z3V3_Z3_app(_v3, &bound[_c2], _ctx); + } + num_bound = _c1; + _c4 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_patterns, _c5); + camlidl_ml2c_z3V3_Z3_pattern(_v6, &patterns[_c5], _ctx); + } + num_patterns = _c4; + camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_exists_const(c, weight, num_bound, bound, num_patterns, patterns, body); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_quantifier_const( + value _v_c, + value _v_is_forall, + value _v_weight, + value _v_bound, + value _v_patterns, + value _v_body) +{ + Z3_context c; /*in*/ + int is_forall; /*in*/ + unsigned int weight; /*in*/ + unsigned int num_bound; /*in*/ + Z3_app const *bound; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + is_forall = Int_val(_v_is_forall); + weight = Int_val(_v_weight); + _c1 = Wosize_val(_v_bound); + bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_bound, _c2); + camlidl_ml2c_z3V3_Z3_app(_v3, &bound[_c2], _ctx); + } + num_bound = _c1; + _c4 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_patterns, _c5); + camlidl_ml2c_z3V3_Z3_pattern(_v6, &patterns[_c5], _ctx); + } + num_patterns = _c4; + camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_quantifier_const(c, is_forall, weight, num_bound, bound, num_patterns, patterns, body); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_quantifier_const_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_mk_quantifier_const(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3V3_Z3_mk_quantifier_const_ex( + value _v_c, + value _v_is_forall, + value _v_weight, + value _v_quantifier_id, + value _v_skolem_id, + value _v_bound, + value _v_patterns, + value _v_no_patterns, + value _v_body) +{ + Z3_context c; /*in*/ + int is_forall; /*in*/ + unsigned int weight; /*in*/ + Z3_symbol quantifier_id; /*in*/ + Z3_symbol skolem_id; /*in*/ + unsigned int num_bound; /*in*/ + Z3_app const *bound; /*in*/ + unsigned int num_patterns; /*in*/ + Z3_pattern const *patterns; /*in*/ + unsigned int num_no_patterns; /*in*/ + Z3_ast const *no_patterns; /*in*/ + Z3_ast body; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + is_forall = Int_val(_v_is_forall); + weight = Int_val(_v_weight); + camlidl_ml2c_z3V3_Z3_symbol(_v_quantifier_id, &quantifier_id, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_skolem_id, &skolem_id, _ctx); + _c1 = Wosize_val(_v_bound); + bound = camlidl_malloc(_c1 * sizeof(Z3_app const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_bound, _c2); + camlidl_ml2c_z3V3_Z3_app(_v3, &bound[_c2], _ctx); + } + num_bound = _c1; + _c4 = Wosize_val(_v_patterns); + patterns = camlidl_malloc(_c4 * sizeof(Z3_pattern const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_patterns, _c5); + camlidl_ml2c_z3V3_Z3_pattern(_v6, &patterns[_c5], _ctx); + } + num_patterns = _c4; + _c7 = Wosize_val(_v_no_patterns); + no_patterns = camlidl_malloc(_c7 * sizeof(Z3_ast const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_no_patterns, _c8); + camlidl_ml2c_z3V3_Z3_ast(_v9, &no_patterns[_c8], _ctx); + } + num_no_patterns = _c7; + camlidl_ml2c_z3V3_Z3_ast(_v_body, &body, _ctx); + _res = Z3_mk_quantifier_const_ex(c, is_forall, weight, quantifier_id, skolem_id, num_bound, bound, num_patterns, patterns, num_no_patterns, no_patterns, body); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_quantifier_const_ex_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_mk_quantifier_const_ex(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); +} + +value camlidl_z3V3_Z3_get_symbol_kind( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + Z3_symbol_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_get_symbol_kind(c, s); + _vres = camlidl_c2ml_z3V3_Z3_symbol_kind(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_symbol_int( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_get_symbol_int(c, s); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_symbol_string( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_get_symbol_string(c, s); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_sort_name( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_sort d; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_d, &d, _ctx); + _res = Z3_get_sort_name(c, d); + _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_sort_id( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_get_sort_id(c, s); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_sort_to_ast( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_sort_to_ast(c, s); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_is_eq_sort( + value _v_c, + value _v_s1, + value _v_s2) +{ + Z3_context c; /*in*/ + Z3_sort s1; /*in*/ + Z3_sort s2; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s1, &s1, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s2, &s2, _ctx); + _res = Z3_is_eq_sort(c, s1, s2); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_sort_kind( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + Z3_sort_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_sort_kind(c, t); + _vres = camlidl_c2ml_z3V3_Z3_sort_kind(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_bv_sort_size( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_bv_sort_size(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_finite_domain_sort_size( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + __int64 *r; /*out*/ + __int64 _c1; + value _v2; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); + r = &_c1; + Z3_get_finite_domain_sort_size(c, s, r); + if (r == NULL) { + _vres = Val_int(0); + } else { + _v2 = copy_int64(*r); + Begin_root(_v2) + _vres = camlidl_alloc_small(1, 0); + Field(_vres, 0) = _v2; + End_roots(); + } + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_array_sort_domain( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_array_sort_domain(c, t); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_array_sort_range( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_array_sort_range(c, t); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_tuple_sort_mk_decl( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_tuple_sort_mk_decl(c, t); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_tuple_sort_num_fields( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_tuple_sort_num_fields(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_tuple_sort_field_decl( + value _v_c, + value _v_t, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int i; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + i = Int_val(_v_i); + _res = Z3_get_tuple_sort_field_decl(c, t, i); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_datatype_sort_num_constructors( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + _res = Z3_get_datatype_sort_num_constructors(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_datatype_sort_constructor( + value _v_c, + value _v_t, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int idx; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_datatype_sort_constructor(c, t, idx); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_datatype_sort_recognizer( + value _v_c, + value _v_t, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int idx; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_datatype_sort_recognizer(c, t, idx); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_datatype_sort_constructor_accessor( + value _v_c, + value _v_t, + value _v_idx_c, + value _v_idx_a) +{ + Z3_context c; /*in*/ + Z3_sort t; /*in*/ + unsigned int idx_c; /*in*/ + unsigned int idx_a; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_t, &t, _ctx); + idx_c = Int_val(_v_idx_c); + idx_a = Int_val(_v_idx_a); + _res = Z3_get_datatype_sort_constructor_accessor(c, t, idx_c, idx_a); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_relation_arity( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_get_relation_arity(c, s); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_relation_column( + value _v_c, + value _v_s, + value _v_col) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + unsigned int col; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); + col = Int_val(_v_col); + _res = Z3_get_relation_column(c, s, col); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_func_decl_to_ast( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_func_decl f; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_f, &f, _ctx); + _res = Z3_func_decl_to_ast(c, f); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_is_eq_func_decl( + value _v_c, + value _v_f1, + value _v_f2) +{ + Z3_context c; /*in*/ + Z3_func_decl f1; /*in*/ + Z3_func_decl f2; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_f1, &f1, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_f2, &f2, _ctx); + _res = Z3_is_eq_func_decl(c, f1, f2); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_func_decl_id( + value _v_c, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_func_decl f; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_f, &f, _ctx); + _res = Z3_get_func_decl_id(c, f); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_name( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_decl_name(c, d); + _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_kind( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + Z3_decl_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_decl_kind(c, d); + _vres = camlidl_c2ml_z3V3_Z3_decl_kind(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_domain_size( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_domain_size(c, d); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_arity( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_arity(c, d); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_domain( + value _v_c, + value _v_d, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int i; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + i = Int_val(_v_i); + _res = Z3_get_domain(c, d, i); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_range( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_range(c, d); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_num_parameters( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_get_decl_num_parameters(c, d); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_parameter_kind( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_parameter_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_parameter_kind(c, d, idx); + _vres = camlidl_c2ml_z3V3_Z3_parameter_kind(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_int_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_int_parameter(c, d, idx); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_double_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + double _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_double_parameter(c, d, idx); + _vres = copy_double(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_symbol_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_symbol_parameter(c, d, idx); + _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_sort_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_sort_parameter(c, d, idx); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_ast_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_ast_parameter(c, d, idx); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_func_decl_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_func_decl_parameter(c, d, idx); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_decl_rational_parameter( + value _v_c, + value _v_d, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int idx; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_decl_rational_parameter(c, d, idx); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_app_to_ast( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_app a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_app(_v_a, &a, _ctx); + _res = Z3_app_to_ast(c, a); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_app_decl( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_app a; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_app(_v_a, &a, _ctx); + _res = Z3_get_app_decl(c, a); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_app_num_args( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_app a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_app(_v_a, &a, _ctx); + _res = Z3_get_app_num_args(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_app_arg( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_app a; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_app(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_app_arg(c, a, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_is_eq_ast( + value _v_c, + value _v_t1, + value _v_t2) +{ + Z3_context c; /*in*/ + Z3_ast t1; /*in*/ + Z3_ast t2; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t1, &t1, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t2, &t2, _ctx); + _res = Z3_is_eq_ast(c, t1, t2); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_ast_id( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_ast t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t, &t, _ctx); + _res = Z3_get_ast_id(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_ast_hash( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_ast_hash(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_sort( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_sort(c, a); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_is_well_sorted( + value _v_c, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_ast t; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t, &t, _ctx); + _res = Z3_is_well_sorted(c, t); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_bool_value( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_lbool _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_bool_value(c, a); + _vres = camlidl_c2ml_z3V3_Z3_lbool(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_ast_kind( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast_kind _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_ast_kind(c, a); + _vres = camlidl_c2ml_z3V3_Z3_ast_kind(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_is_app( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_app(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_is_numeral_ast( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_numeral_ast(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_is_algebraic_number( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_algebraic_number(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_to_app( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_app _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_to_app(c, a); + _vres = camlidl_c2ml_z3V3_Z3_app(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_to_func_decl( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_to_func_decl(c, a); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_numeral_string( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_numeral_string(c, a); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_numeral_decimal_string( + value _v_c, + value _v_a, + value _v_precision) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int precision; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + precision = Int_val(_v_precision); + _res = Z3_get_numeral_decimal_string(c, a, precision); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_numerator( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_numerator(c, a); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_denominator( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_denominator(c, a); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_numeral_small( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + __int64 *num; /*out*/ + __int64 *den; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + __int64 _c1; + __int64 _c2; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + num = &_c1; + den = &_c2; + _res = Z3_get_numeral_small(c, a, num, den); + Begin_roots_block(_vres, 3) + _vres[0] = Val_int(_res); + _vres[1] = copy_int64(*num); + _vres[2] = copy_int64(*den); + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_get_numeral_int( + value _v_c, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast v; /*in*/ + int *i; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + int _c1; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); + i = &_c1; + _res = Z3_get_numeral_int(c, v, i); + Begin_roots_block(_vres, 2) + _vres[0] = Val_int(_res); + _vres[1] = Val_int(*i); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_get_numeral_int64( + value _v_c, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast v; /*in*/ + __int64 *i; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + __int64 _c1; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); + i = &_c1; + _res = Z3_get_numeral_int64(c, v, i); + Begin_roots_block(_vres, 2) + _vres[0] = Val_int(_res); + _vres[1] = copy_int64(*i); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_get_numeral_rational_int64( + value _v_c, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_ast v; /*in*/ + __int64 *num; /*out*/ + __int64 *den; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + __int64 _c1; + __int64 _c2; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); + num = &_c1; + den = &_c2; + _res = Z3_get_numeral_rational_int64(c, v, num, den); + Begin_roots_block(_vres, 3) + _vres[0] = Val_int(_res); + _vres[1] = copy_int64(*num); + _vres[2] = copy_int64(*den); + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_get_algebraic_number_lower( + value _v_c, + value _v_a, + value _v_precision) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int precision; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + precision = Int_val(_v_precision); + _res = Z3_get_algebraic_number_lower(c, a, precision); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_algebraic_number_upper( + value _v_c, + value _v_a, + value _v_precision) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int precision; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + precision = Int_val(_v_precision); + _res = Z3_get_algebraic_number_upper(c, a, precision); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_pattern_to_ast( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_pattern p; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_pattern(_v_p, &p, _ctx); + _res = Z3_pattern_to_ast(c, p); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_pattern_num_terms( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_pattern p; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_pattern(_v_p, &p, _ctx); + _res = Z3_get_pattern_num_terms(c, p); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_pattern( + value _v_c, + value _v_p, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_pattern p; /*in*/ + unsigned int idx; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_pattern(_v_p, &p, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_pattern(c, p, idx); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_index_value( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_index_value(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_is_quantifier_forall( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_is_quantifier_forall(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_weight( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_weight(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_num_patterns( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_num_patterns(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_pattern_ast( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int i; /*in*/ + Z3_pattern _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_quantifier_pattern_ast(c, a, i); + _vres = camlidl_c2ml_z3V3_Z3_pattern(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_num_no_patterns( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_num_no_patterns(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_no_pattern_ast( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_quantifier_no_pattern_ast(c, a, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_num_bound( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_num_bound(c, a); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_bound_name( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int i; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_quantifier_bound_name(c, a, i); + _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_bound_sort( + value _v_c, + value _v_a, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int i; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + i = Int_val(_v_i); + _res = Z3_get_quantifier_bound_sort(c, a, i); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_quantifier_body( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_get_quantifier_body(c, a); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_simplify( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_simplify(c, a); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_update_term( + value _v_c, + value _v_a, + value _v_args) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + _res = Z3_update_term(c, a, num_args, args); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_substitute( + value _v_c, + value _v_a, + value _v_from, + value _v_to) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int num_exprs; /*in*/ + Z3_ast const *from; /*in*/ + Z3_ast const *to; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _c1 = Wosize_val(_v_from); + from = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_from, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &from[_c2], _ctx); + } + num_exprs = _c1; + _c4 = Wosize_val(_v_to); + to = camlidl_malloc(_c4 * sizeof(Z3_ast const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_to, _c5); + camlidl_ml2c_z3V3_Z3_ast(_v6, &to[_c5], _ctx); + } + num_exprs = _c4; + _res = Z3_substitute(c, a, num_exprs, from, to); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_substitute_vars( + value _v_c, + value _v_a, + value _v_to) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int num_exprs; /*in*/ + Z3_ast const *to; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _c1 = Wosize_val(_v_to); + to = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_to, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &to[_c2], _ctx); + } + num_exprs = _c1; + _res = Z3_substitute_vars(c, a, num_exprs, to); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_open_log( + value _v_filename) +{ + Z3_string filename; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_string(_v_filename, &filename, _ctx); + _res = Z3_open_log(filename); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_append_log( + value _v_string) +{ + Z3_string string; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_string(_v_string, &string, _ctx); + Z3_append_log(string); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_close_log(value _unit) +{ + Z3_close_log(); + return Val_unit; +} + +value camlidl_z3V3_Z3_toggle_warning_messages( + value _v_enabled) +{ + int enabled; /*in*/ + enabled = Int_val(_v_enabled); + Z3_toggle_warning_messages(enabled); + return Val_unit; +} + +value camlidl_z3V3_Z3_set_ast_print_mode( + value _v_c, + value _v_mode) +{ + Z3_context c; /*in*/ + Z3_ast_print_mode mode; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast_print_mode(_v_mode, &mode, _ctx); + Z3_set_ast_print_mode(c, mode); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_ast_to_string( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + _res = Z3_ast_to_string(c, a); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_pattern_to_string( + value _v_c, + value _v_p) +{ + Z3_context c; /*in*/ + Z3_pattern p; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_pattern(_v_p, &p, _ctx); + _res = Z3_pattern_to_string(c, p); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_sort_to_string( + value _v_c, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_sort s; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_sort_to_string(c, s); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_func_decl_to_string( + value _v_c, + value _v_d) +{ + Z3_context c; /*in*/ + Z3_func_decl d; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_func_decl_to_string(c, d); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_model_to_string( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + _res = Z3_model_to_string(c, m); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_benchmark_to_smtlib_string( + value _v_c, + value _v_name, + value _v_logic, + value _v_status, + value _v_attributes, + value _v_assumptions, + value _v_formula) +{ + Z3_context c; /*in*/ + Z3_string name; /*in*/ + Z3_string logic; /*in*/ + Z3_string status; /*in*/ + Z3_string attributes; /*in*/ + unsigned int num_assumptions; /*in*/ + Z3_ast const *assumptions; /*in*/ + Z3_ast formula; /*in*/ + Z3_string _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_name, &name, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_logic, &logic, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_status, &status, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_attributes, &attributes, _ctx); + _c1 = Wosize_val(_v_assumptions); + assumptions = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_assumptions, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &assumptions[_c2], _ctx); + } + num_assumptions = _c1; + camlidl_ml2c_z3V3_Z3_ast(_v_formula, &formula, _ctx); + _res = Z3_benchmark_to_smtlib_string(c, name, logic, status, attributes, num_assumptions, assumptions, formula); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_benchmark_to_smtlib_string_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_benchmark_to_smtlib_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); +} + +value camlidl_z3V3_Z3_parse_smtlib2_string( + value _v_c, + value _v_str, + value _v_sort_names, + value _v_sorts, + value _v_decl_names, + value _v_decls) +{ + Z3_context c; /*in*/ + Z3_string str; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int num_decls; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_func_decl const *decls; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_str, &str, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_sorts = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decls); + decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decls, _c11); + camlidl_ml2c_z3V3_Z3_func_decl(_v12, &decls[_c11], _ctx); + } + num_decls = _c10; + _res = Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_parse_smtlib2_string_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_parse_smtlib2_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3V3_Z3_parse_smtlib2_file( + value _v_c, + value _v_file_name, + value _v_sort_names, + value _v_sorts, + value _v_decl_names, + value _v_decls) +{ + Z3_context c; /*in*/ + Z3_string file_name; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int num_decls; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_func_decl const *decls; /*in*/ + Z3_ast _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_file_name, &file_name, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_sorts = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decls); + decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decls, _c11); + camlidl_ml2c_z3V3_Z3_func_decl(_v12, &decls[_c11], _ctx); + } + num_decls = _c10; + _res = Z3_parse_smtlib2_file(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_parse_smtlib2_file_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_parse_smtlib2_file(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3V3_Z3_parse_smtlib_string( + value _v_c, + value _v_str, + value _v_sort_names, + value _v_sorts, + value _v_decl_names, + value _v_decls) +{ + Z3_context c; /*in*/ + Z3_string str; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int num_decls; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_func_decl const *decls; /*in*/ + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_str, &str, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_sorts = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decls); + decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decls, _c11); + camlidl_ml2c_z3V3_Z3_func_decl(_v12, &decls[_c11], _ctx); + } + num_decls = _c10; + Z3_parse_smtlib_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_parse_smtlib_string_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_parse_smtlib_string(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3V3_Z3_parse_smtlib_file( + value _v_c, + value _v_file_name, + value _v_sort_names, + value _v_sorts, + value _v_decl_names, + value _v_decls) +{ + Z3_context c; /*in*/ + Z3_string file_name; /*in*/ + unsigned int num_sorts; /*in*/ + Z3_symbol const *sort_names; /*in*/ + Z3_sort const *sorts; /*in*/ + unsigned int num_decls; /*in*/ + Z3_symbol const *decl_names; /*in*/ + Z3_func_decl const *decls; /*in*/ + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + mlsize_t _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + mlsize_t _c11; + value _v12; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_file_name, &file_name, _ctx); + _c1 = Wosize_val(_v_sort_names); + sort_names = camlidl_malloc(_c1 * sizeof(Z3_symbol const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_sort_names, _c2); + camlidl_ml2c_z3V3_Z3_symbol(_v3, &sort_names[_c2], _ctx); + } + num_sorts = _c1; + _c4 = Wosize_val(_v_sorts); + sorts = camlidl_malloc(_c4 * sizeof(Z3_sort const ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_sorts, _c5); + camlidl_ml2c_z3V3_Z3_sort(_v6, &sorts[_c5], _ctx); + } + num_sorts = _c4; + _c7 = Wosize_val(_v_decl_names); + decl_names = camlidl_malloc(_c7 * sizeof(Z3_symbol const ), _ctx); + for (_c8 = 0; _c8 < _c7; _c8++) { + _v9 = Field(_v_decl_names, _c8); + camlidl_ml2c_z3V3_Z3_symbol(_v9, &decl_names[_c8], _ctx); + } + num_decls = _c7; + _c10 = Wosize_val(_v_decls); + decls = camlidl_malloc(_c10 * sizeof(Z3_func_decl const ), _ctx); + for (_c11 = 0; _c11 < _c10; _c11++) { + _v12 = Field(_v_decls, _c11); + camlidl_ml2c_z3V3_Z3_func_decl(_v12, &decls[_c11], _ctx); + } + num_decls = _c10; + Z3_parse_smtlib_file(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_parse_smtlib_file_bytecode(value * argv, int argn) +{ + return camlidl_z3V3_Z3_parse_smtlib_file(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); +} + +value camlidl_z3V3_Z3_get_smtlib_num_formulas( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_num_formulas(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_smtlib_formula( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_smtlib_formula(c, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_smtlib_num_assumptions( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_num_assumptions(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_smtlib_assumption( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_smtlib_assumption(c, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_smtlib_num_decls( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_num_decls(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_smtlib_decl( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_smtlib_decl(c, i); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_smtlib_num_sorts( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_num_sorts(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_smtlib_sort( + value _v_c, + value _v_i) +{ + Z3_context c; /*in*/ + unsigned int i; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + i = Int_val(_v_i); + _res = Z3_get_smtlib_sort(c, i); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_smtlib_error( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_smtlib_error(c); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} +/* +value camlidl_z3_Z3_parse_z3V3_string( + value _v_c, + value _v_str) +{ + Z3_context c; /*in + Z3_string str; /*in + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_str, &str, _ctx); + _res = Z3_parse_z3_string(c, str); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3_Z3_parse_z3V3_file( + value _v_c, + value _v_file_name) +{ + Z3_context c; /*in + Z3_string file_name; /*in + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_file_name, &file_name, _ctx); + _res = Z3_parse_z3_file(c, file_name); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} +*/ +value camlidl_z3V3_Z3_get_version(value _unit) +{ + unsigned int *major; /*out*/ + unsigned int *minor; /*out*/ + unsigned int *build_number; /*out*/ + unsigned int *revision_number; /*out*/ + unsigned int _c1; + unsigned int _c2; + unsigned int _c3; + unsigned int _c4; + value _vresult; + value _vres[4] = { 0, 0, 0, 0, }; + + major = &_c1; + minor = &_c2; + build_number = &_c3; + revision_number = &_c4; + Z3_get_version(major, minor, build_number, revision_number); + Begin_roots_block(_vres, 4) + _vres[0] = Val_int(*major); + _vres[1] = Val_int(*minor); + _vres[2] = Val_int(*build_number); + _vres[3] = Val_int(*revision_number); + _vresult = camlidl_alloc_small(4, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + Field(_vresult, 3) = _vres[3]; + End_roots() + return _vresult; +} + +value camlidl_z3V3_Z3_reset_memory(value _unit) +{ + Z3_reset_memory(); + return Val_unit; +} + +value camlidl_z3V3_Z3_theory_mk_sort( + value _v_c, + value _v_t, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_theory t; /*in*/ + Z3_symbol s; /*in*/ + Z3_sort _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + _res = Z3_theory_mk_sort(c, t, s); + _vres = camlidl_c2ml_z3V3_Z3_sort(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_mk_value( + value _v_c, + value _v_t, + value _v_n, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_theory t; /*in*/ + Z3_symbol n; /*in*/ + Z3_sort s; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_n, &n, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_theory_mk_value(c, t, n, s); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_mk_constant( + value _v_c, + value _v_t, + value _v_n, + value _v_s) +{ + Z3_context c; /*in*/ + Z3_theory t; /*in*/ + Z3_symbol n; /*in*/ + Z3_sort s; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_n, &n, _ctx); + camlidl_ml2c_z3V3_Z3_sort(_v_s, &s, _ctx); + _res = Z3_theory_mk_constant(c, t, n, s); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_mk_func_decl( + value _v_c, + value _v_t, + value _v_n, + value _v_domain, + value _v_range) +{ + Z3_context c; /*in*/ + Z3_theory t; /*in*/ + Z3_symbol n; /*in*/ + unsigned int domain_size; /*in*/ + Z3_sort const *domain; /*in*/ + Z3_sort range; /*in*/ + Z3_func_decl _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_n, &n, _ctx); + _c1 = Wosize_val(_v_domain); + domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_domain, _c2); + camlidl_ml2c_z3V3_Z3_sort(_v3, &domain[_c2], _ctx); + } + domain_size = _c1; + camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); + _res = Z3_theory_mk_func_decl(c, t, n, domain_size, domain, range); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_get_context( + value _v_t) +{ + Z3_theory t; /*in*/ + Z3_context _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + _res = Z3_theory_get_context(t); + _vres = camlidl_c2ml_z3V3_Z3_context(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_assert_axiom( + value _v_t, + value _v_ax) +{ + Z3_theory t; /*in*/ + Z3_ast ax; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_ax, &ax, _ctx); + Z3_theory_assert_axiom(t, ax); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_theory_assume_eq( + value _v_t, + value _v_lhs, + value _v_rhs) +{ + Z3_theory t; /*in*/ + Z3_ast lhs; /*in*/ + Z3_ast rhs; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_lhs, &lhs, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_rhs, &rhs, _ctx); + Z3_theory_assume_eq(t, lhs, rhs); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_theory_enable_axiom_simplification( + value _v_t, + value _v_flag) +{ + Z3_theory t; /*in*/ + int flag; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + flag = Int_val(_v_flag); + Z3_theory_enable_axiom_simplification(t, flag); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_theory_get_eqc_root( + value _v_t, + value _v_n) +{ + Z3_theory t; /*in*/ + Z3_ast n; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); + _res = Z3_theory_get_eqc_root(t, n); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_get_eqc_next( + value _v_t, + value _v_n) +{ + Z3_theory t; /*in*/ + Z3_ast n; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); + _res = Z3_theory_get_eqc_next(t, n); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_get_num_parents( + value _v_t, + value _v_n) +{ + Z3_theory t; /*in*/ + Z3_ast n; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); + _res = Z3_theory_get_num_parents(t, n); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_get_parent( + value _v_t, + value _v_n, + value _v_i) +{ + Z3_theory t; /*in*/ + Z3_ast n; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); + i = Int_val(_v_i); + _res = Z3_theory_get_parent(t, n, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_is_value( + value _v_t, + value _v_n) +{ + Z3_theory t; /*in*/ + Z3_ast n; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_n, &n, _ctx); + _res = Z3_theory_is_value(t, n); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_is_decl( + value _v_t, + value _v_d) +{ + Z3_theory t; /*in*/ + Z3_func_decl d; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _res = Z3_theory_is_decl(t, d); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_get_num_elems( + value _v_t) +{ + Z3_theory t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + _res = Z3_theory_get_num_elems(t); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_get_elem( + value _v_t, + value _v_i) +{ + Z3_theory t; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + i = Int_val(_v_i); + _res = Z3_theory_get_elem(t, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_get_num_apps( + value _v_t) +{ + Z3_theory t; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + _res = Z3_theory_get_num_apps(t); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_theory_get_app( + value _v_t, + value _v_i) +{ + Z3_theory t; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_theory(_v_t, &t, _ctx); + i = Int_val(_v_i); + _res = Z3_theory_get_app(t, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_injective_function( + value _v_c, + value _v_s, + value _v_domain, + value _v_range) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + unsigned int domain_size; /*in*/ + Z3_sort const *domain; /*in*/ + Z3_sort range; /*in*/ + Z3_func_decl _res; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + _c1 = Wosize_val(_v_domain); + domain = camlidl_malloc(_c1 * sizeof(Z3_sort const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_domain, _c2); + camlidl_ml2c_z3V3_Z3_sort(_v3, &domain[_c2], _ctx); + } + domain_size = _c1; + camlidl_ml2c_z3V3_Z3_sort(_v_range, &range, _ctx); + _res = Z3_mk_injective_function(c, s, domain_size, domain, range); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_set_logic( + value _v_c, + value _v_logic) +{ + Z3_context c; /*in*/ + Z3_string logic; /*in*/ + int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_string(_v_logic, &logic, _ctx); + _res = Z3_set_logic(c, logic); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_push( + value _v_c) +{ + Z3_context c; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + Z3_push(c); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_pop( + value _v_c, + value _v_num_scopes) +{ + Z3_context c; /*in*/ + unsigned int num_scopes; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + num_scopes = Int_val(_v_num_scopes); + Z3_pop(c, num_scopes); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_get_num_scopes( + value _v_c) +{ + Z3_context c; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_num_scopes(c); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_persist_ast( + value _v_c, + value _v_a, + value _v_num_scopes) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + unsigned int num_scopes; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + num_scopes = Int_val(_v_num_scopes); + Z3_persist_ast(c, a, num_scopes); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_assert_cnstr( + value _v_c, + value _v_a) +{ + Z3_context c; /*in*/ + Z3_ast a; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_a, &a, _ctx); + Z3_assert_cnstr(c, a); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_check_and_get_model( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_model *m; /*out*/ + Z3_lbool _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + Z3_model _c1; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + m = &_c1; + _res = Z3_check_and_get_model(c, m); + Begin_roots_block(_vres, 2) + _vres[0] = camlidl_c2ml_z3V3_Z3_lbool(&_res, _ctx); + _vres[1] = camlidl_c2ml_z3V3_Z3_model(&*m, _ctx); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_check( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_lbool _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_check(c); + _vres = camlidl_c2ml_z3V3_Z3_lbool(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_check_assumptions( + value _v_c, + value _v_assumptions, + value _v_core_size, + value _v_core) +{ + Z3_context c; /*in*/ + unsigned int num_assumptions; /*in*/ + Z3_ast const *assumptions; /*in*/ + Z3_model *m; /*out*/ + Z3_ast *proof; /*out*/ + unsigned int *core_size; /*in,out*/ + Z3_ast *core; /*in,out*/ + Z3_lbool _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + unsigned int _c4; + mlsize_t _c5; + mlsize_t _c6; + value _v7; + Z3_model _c8; + Z3_ast _c9; + mlsize_t _c10; + value _v11; + value _vresult; + value _vres[5] = { 0, 0, 0, 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _c1 = Wosize_val(_v_assumptions); + assumptions = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_assumptions, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &assumptions[_c2], _ctx); + } + num_assumptions = _c1; + core_size = &_c4; + _c4 = Int_val(_v_core_size); + _c5 = Wosize_val(_v_core); + core = camlidl_malloc(_c5 * sizeof(Z3_ast ), _ctx); + for (_c6 = 0; _c6 < _c5; _c6++) { + _v7 = Field(_v_core, _c6); + camlidl_ml2c_z3V3_Z3_ast(_v7, &core[_c6], _ctx); + } + num_assumptions = _c5; + m = &_c8; + proof = &_c9; + _res = Z3_check_assumptions(c, num_assumptions, assumptions, m, proof, core_size, core); + Begin_roots_block(_vres, 5) + _vres[0] = camlidl_c2ml_z3V3_Z3_lbool(&_res, _ctx); + _vres[1] = camlidl_c2ml_z3V3_Z3_model(&*m, _ctx); + _vres[2] = camlidl_c2ml_z3V3_Z3_ast(&*proof, _ctx); + _vres[3] = Val_int(*core_size); + _vres[4] = camlidl_alloc(num_assumptions, 0); + Begin_root(_vres[4]) + for (_c10 = 0; _c10 < num_assumptions; _c10++) { + _v11 = camlidl_c2ml_z3V3_Z3_ast(&core[_c10], _ctx); + modify(&Field(_vres[4], _c10), _v11); + } + End_roots() + _vresult = camlidl_alloc_small(5, 0); + { mlsize_t _c12; + for (_c12 = 0; _c12 < 5; _c12++) Field(_vresult, _c12) = _vres[_c12]; + } + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_del_model( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + Z3_del_model(c, m); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_soft_check_cancel( + value _v_c) +{ + Z3_context c; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + Z3_soft_check_cancel(c); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_get_search_failure( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_search_failure _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_search_failure(c); + _vres = camlidl_c2ml_z3V3_Z3_search_failure(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_mk_label( + value _v_c, + value _v_s, + value _v_is_pos, + value _v_f) +{ + Z3_context c; /*in*/ + Z3_symbol s; /*in*/ + int is_pos; /*in*/ + Z3_ast f; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_symbol(_v_s, &s, _ctx); + is_pos = Int_val(_v_is_pos); + camlidl_ml2c_z3V3_Z3_ast(_v_f, &f, _ctx); + _res = Z3_mk_label(c, s, is_pos, f); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_relevant_labels( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_literals _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_relevant_labels(c); + _vres = camlidl_c2ml_z3V3_Z3_literals(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_relevant_literals( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_literals _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_relevant_literals(c); + _vres = camlidl_c2ml_z3V3_Z3_literals(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_guessed_literals( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_literals _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_guessed_literals(c); + _vres = camlidl_c2ml_z3V3_Z3_literals(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_del_literals( + value _v_c, + value _v_lbls) +{ + Z3_context c; /*in*/ + Z3_literals lbls; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); + Z3_del_literals(c, lbls); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_get_num_literals( + value _v_c, + value _v_lbls) +{ + Z3_context c; /*in*/ + Z3_literals lbls; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); + _res = Z3_get_num_literals(c, lbls); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_label_symbol( + value _v_c, + value _v_lbls, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_literals lbls; /*in*/ + unsigned int idx; /*in*/ + Z3_symbol _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_label_symbol(c, lbls, idx); + _vres = camlidl_c2ml_z3V3_Z3_symbol(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_literal( + value _v_c, + value _v_lbls, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_literals lbls; /*in*/ + unsigned int idx; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); + idx = Int_val(_v_idx); + _res = Z3_get_literal(c, lbls, idx); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_disable_literal( + value _v_c, + value _v_lbls, + value _v_idx) +{ + Z3_context c; /*in*/ + Z3_literals lbls; /*in*/ + unsigned int idx; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); + idx = Int_val(_v_idx); + Z3_disable_literal(c, lbls, idx); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_block_literals( + value _v_c, + value _v_lbls) +{ + Z3_context c; /*in*/ + Z3_literals lbls; /*in*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_literals(_v_lbls, &lbls, _ctx); + Z3_block_literals(c, lbls); + camlidl_free(_ctx); + return Val_unit; +} + +value camlidl_z3V3_Z3_get_model_num_constants( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + _res = Z3_get_model_num_constants(c, m); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_model_constant( + value _v_c, + value _v_m, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + _res = Z3_get_model_constant(c, m, i); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_model_num_funcs( + value _v_c, + value _v_m) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + _res = Z3_get_model_num_funcs(c, m); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_model_func_decl( + value _v_c, + value _v_m, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + Z3_func_decl _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + _res = Z3_get_model_func_decl(c, m, i); + _vres = camlidl_c2ml_z3V3_Z3_func_decl(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_eval_func_decl( + value _v_c, + value _v_m, + value _v_decl) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_func_decl decl; /*in*/ + Z3_ast *v; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + Z3_ast _c1; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_decl, &decl, _ctx); + v = &_c1; + _res = Z3_eval_func_decl(c, m, decl, v); + Begin_roots_block(_vres, 2) + _vres[0] = Val_int(_res); + _vres[1] = camlidl_c2ml_z3V3_Z3_ast(&*v, _ctx); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_is_array_value( + value _v_c, + value _v_m, + value _v_v) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_ast v; /*in*/ + unsigned int *num_entries; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + unsigned int _c1; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); + num_entries = &_c1; + _res = Z3_is_array_value(c, m, v, num_entries); + Begin_roots_block(_vres, 2) + _vres[0] = Val_int(_res); + _vres[1] = Val_int(*num_entries); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_get_array_value( + value _v_c, + value _v_m, + value _v_v, + value _v_indices, + value _v_values) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_ast v; /*in*/ + unsigned int num_entries; /*in*/ + Z3_ast *indices; /*in,out*/ + Z3_ast *values; /*in,out*/ + Z3_ast *else_value; /*out*/ + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + mlsize_t _c4; + mlsize_t _c5; + value _v6; + Z3_ast _c7; + mlsize_t _c8; + value _v9; + mlsize_t _c10; + value _v11; + value _vresult; + value _vres[3] = { 0, 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_v, &v, _ctx); + _c1 = Wosize_val(_v_indices); + indices = camlidl_malloc(_c1 * sizeof(Z3_ast ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_indices, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &indices[_c2], _ctx); + } + num_entries = _c1; + _c4 = Wosize_val(_v_values); + values = camlidl_malloc(_c4 * sizeof(Z3_ast ), _ctx); + for (_c5 = 0; _c5 < _c4; _c5++) { + _v6 = Field(_v_values, _c5); + camlidl_ml2c_z3V3_Z3_ast(_v6, &values[_c5], _ctx); + } + num_entries = _c4; + else_value = &_c7; + Z3_get_array_value(c, m, v, num_entries, indices, values, else_value); + Begin_roots_block(_vres, 3) + _vres[0] = camlidl_alloc(num_entries, 0); + Begin_root(_vres[0]) + for (_c8 = 0; _c8 < num_entries; _c8++) { + _v9 = camlidl_c2ml_z3V3_Z3_ast(&indices[_c8], _ctx); + modify(&Field(_vres[0], _c8), _v9); + } + End_roots() + _vres[1] = camlidl_alloc(num_entries, 0); + Begin_root(_vres[1]) + for (_c10 = 0; _c10 < num_entries; _c10++) { + _v11 = camlidl_c2ml_z3V3_Z3_ast(&values[_c10], _ctx); + modify(&Field(_vres[1], _c10), _v11); + } + End_roots() + _vres[2] = camlidl_c2ml_z3V3_Z3_ast(&*else_value, _ctx); + _vresult = camlidl_alloc_small(3, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + Field(_vresult, 2) = _vres[2]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_get_model_func_else( + value _v_c, + value _v_m, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + _res = Z3_get_model_func_else(c, m, i); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_model_func_num_entries( + value _v_c, + value _v_m, + value _v_i) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + _res = Z3_get_model_func_num_entries(c, m, i); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_model_func_entry_num_args( + value _v_c, + value _v_m, + value _v_i, + value _v_j) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + unsigned int j; /*in*/ + unsigned int _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + j = Int_val(_v_j); + _res = Z3_get_model_func_entry_num_args(c, m, i, j); + _vres = Val_int(_res); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_model_func_entry_arg( + value _v_c, + value _v_m, + value _v_i, + value _v_j, + value _v_k) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + unsigned int j; /*in*/ + unsigned int k; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + j = Int_val(_v_j); + k = Int_val(_v_k); + _res = Z3_get_model_func_entry_arg(c, m, i, j, k); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_model_func_entry_value( + value _v_c, + value _v_m, + value _v_i, + value _v_j) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + unsigned int i; /*in*/ + unsigned int j; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + i = Int_val(_v_i); + j = Int_val(_v_j); + _res = Z3_get_model_func_entry_value(c, m, i, j); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_eval( + value _v_c, + value _v_m, + value _v_t) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_ast t; /*in*/ + Z3_ast *v; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + Z3_ast _c1; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3V3_Z3_ast(_v_t, &t, _ctx); + v = &_c1; + _res = Z3_eval(c, m, t, v); + Begin_roots_block(_vres, 2) + _vres[0] = Val_int(_res); + _vres[1] = camlidl_c2ml_z3V3_Z3_ast(&*v, _ctx); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_eval_decl( + value _v_c, + value _v_m, + value _v_d, + value _v_args) +{ + Z3_context c; /*in*/ + Z3_model m; /*in*/ + Z3_func_decl d; /*in*/ + unsigned int num_args; /*in*/ + Z3_ast const *args; /*in*/ + Z3_ast *v; /*out*/ + int _res; + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + mlsize_t _c1; + mlsize_t _c2; + value _v3; + Z3_ast _c4; + value _vresult; + value _vres[2] = { 0, 0, }; + + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + camlidl_ml2c_z3V3_Z3_model(_v_m, &m, _ctx); + camlidl_ml2c_z3V3_Z3_func_decl(_v_d, &d, _ctx); + _c1 = Wosize_val(_v_args); + args = camlidl_malloc(_c1 * sizeof(Z3_ast const ), _ctx); + for (_c2 = 0; _c2 < _c1; _c2++) { + _v3 = Field(_v_args, _c2); + camlidl_ml2c_z3V3_Z3_ast(_v3, &args[_c2], _ctx); + } + num_args = _c1; + v = &_c4; + _res = Z3_eval_decl(c, m, d, num_args, args, v); + Begin_roots_block(_vres, 2) + _vres[0] = Val_int(_res); + _vres[1] = camlidl_c2ml_z3V3_Z3_ast(&*v, _ctx); + _vresult = camlidl_alloc_small(2, 0); + Field(_vresult, 0) = _vres[0]; + Field(_vresult, 1) = _vres[1]; + End_roots() + camlidl_free(_ctx); + return _vresult; +} + +value camlidl_z3V3_Z3_context_to_string( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_context_to_string(c); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_statistics_to_string( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_string _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_statistics_to_string(c); + _vres = camlidl_c2ml_z3V3_Z3_string(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + +value camlidl_z3V3_Z3_get_context_assignment( + value _v_c) +{ + Z3_context c; /*in*/ + Z3_ast _res; + value _vres; + + struct camlidl_ctx_struct _ctxs = { CAMLIDL_TRANSIENT, NULL }; + camlidl_ctx _ctx = &_ctxs; + camlidl_ml2c_z3V3_Z3_context(_v_c, &c, _ctx); + _res = Z3_get_context_assignment(c); + _vres = camlidl_c2ml_z3V3_Z3_ast(&_res, _ctx); + camlidl_free(_ctx); + return _vres; +} + diff --git a/src/api/python/z3.py b/src/api/python/z3.py index ce1cc2103..f6a617317 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: @@ -5603,7 +5617,7 @@ class Statistics: sat >>> st = s.statistics() >>> len(st) - 2 + 4 """ return int(Z3_stats_size(self.ctx.ref(), self.stats)) @@ -5617,7 +5631,7 @@ class Statistics: sat >>> st = s.statistics() >>> len(st) - 2 + 4 >>> st[0] ('nlsat propagations', 2) >>> st[1] @@ -5641,7 +5655,7 @@ class Statistics: sat >>> st = s.statistics() >>> st.keys() - ['nlsat propagations', 'nlsat stages'] + ['nlsat propagations', 'nlsat stages', 'max memory', 'memory'] """ return [Z3_stats_get_key(self.ctx.ref(), self.stats, idx) for idx in range(len(self))] @@ -5678,7 +5692,7 @@ class Statistics: sat >>> st = s.statistics() >>> st.keys() - ['nlsat propagations', 'nlsat stages'] + ['nlsat propagations', 'nlsat stages', 'max memory', 'memory'] >>> st.nlsat_propagations 2 >>> st.nlsat_stages @@ -6071,8 +6085,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 +6345,166 @@ 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 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 +8194,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/z3types.py b/src/api/python/z3types.py index 5d59368ff..e01b78238 100644 --- a/src/api/python/z3types.py +++ b/src/api/python/z3types.py @@ -78,6 +78,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 new file mode 100644 index 000000000..e0e9aba32 --- /dev/null +++ b/src/api/python/z3util.py @@ -0,0 +1,468 @@ +""" +Usage: +import common_z3 as CM_Z3 +""" + +import common as CM +from z3 import * + +def get_z3_version(as_str=False): + major = ctypes.c_uint(0) + minor = ctypes.c_uint(0) + build = ctypes.c_uint(0) + rev = ctypes.c_uint(0) + Z3_get_version(major,minor,build,rev) + rs = map(int,(major.value,minor.value,build.value,rev.value)) + if as_str: + return "{}.{}.{}.{}".format(*rs) + else: + return rs + + + + + +def ehash(v): + """ + Returns a 'stronger' hash value than the default hash() method. + The result from hash() is not enough to distinguish between 2 + z3 expressions in some cases. + + >>> x1 = Bool('x'); x2 = Bool('x'); x3 = Int('x') + >>> print x1.hash(),x2.hash(),x3.hash() #BAD: all same hash values + 783810685 783810685 783810685 + >>> print ehash(x1), ehash(x2), ehash(x3) + x_783810685_1 x_783810685_1 x_783810685_2 + + """ + if __debug__: + assert is_expr(v) + + return "{}_{}_{}".format(str(v),v.hash(),v.sort_kind()) + + +""" +In Z3, variables are caleld *uninterpreted* consts and +variables are *interpreted* consts. +""" + +def is_expr_var(v): + """ + EXAMPLES: + + >>> is_expr_var(Int('7')) + True + >>> is_expr_var(IntVal('7')) + False + >>> is_expr_var(Bool('y')) + True + >>> is_expr_var(Int('x') + 7 == Int('y')) + False + >>> LOnOff, (On,Off) = EnumSort("LOnOff",['On','Off']) + >>> Block,Reset,SafetyInjection=Consts("Block Reset SafetyInjection",LOnOff) + >>> is_expr_var(LOnOff) + False + >>> is_expr_var(On) + False + >>> is_expr_var(Block) + True + >>> is_expr_var(SafetyInjection) + True + """ + + return is_const(v) and v.decl().kind()==Z3_OP_UNINTERPRETED + +def is_expr_val(v): + """ + EXAMPLES: + + >>> is_expr_val(Int('7')) + False + >>> is_expr_val(IntVal('7')) + True + >>> is_expr_val(Bool('y')) + False + >>> is_expr_val(Int('x') + 7 == Int('y')) + False + >>> LOnOff, (On,Off) = EnumSort("LOnOff",['On','Off']) + >>> Block,Reset,SafetyInjection=Consts("Block Reset SafetyInjection",LOnOff) + >>> is_expr_val(LOnOff) + False + >>> is_expr_val(On) + True + >>> is_expr_val(Block) + False + >>> is_expr_val(SafetyInjection) + False + """ + return is_const(v) and v.decl().kind()!=Z3_OP_UNINTERPRETED + + + + +def get_vars(f,rs=[]): + """ + >>> x,y = Ints('x y') + >>> a,b = Bools('a b') + >>> get_vars(Implies(And(x+y==0,x*2==10),Or(a,Implies(a,b==False)))) + [x, y, a, b] + + """ + if __debug__: + assert is_expr(f) + + if is_const(f): + if is_expr_val(f): + return rs + else: #variable + return CM.vset(rs + [f],str) + + else: + for f_ in f.children(): + rs = get_vars(f_,rs) + + return CM.vset(rs,str) + + + +def mk_var(name,vsort): + if vsort.kind() == Z3_INT_SORT: + v = Int(name) + elif vsort.kind() == Z3_REAL_SORT: + v = Real(name) + elif vsort.kind() == Z3_BOOL_SORT: + v = Bool(name) + elif vsort.kind() == Z3_DATATYPE_SORT: + v = Const(name,vsort) + + else: + assert False, 'Cannot handle this sort (s: %sid: %d)'\ + %(vsort,vsort.kind()) + + return v + + + +def prove(claim,assume=None,verbose=0): + """ + >>> r,m = prove(BoolVal(True),verbose=0); r,model_str(m,as_str=False) + (True, None) + + #infinite counter example when proving contradiction + >>> r,m = prove(BoolVal(False)); r,model_str(m,as_str=False) + (False, []) + + >>> x,y,z=Bools('x y z') + >>> r,m = prove(And(x,Not(x))); r,model_str(m,as_str=True) + (False, '[]') + + >>> r,m = prove(True,assume=And(x,Not(x)),verbose=0) + Traceback (most recent call last): + ... + AssertionError: Assumption is alway False! + + >>> r,m = prove(Implies(x,x),assume=y,verbose=2); r,model_str(m,as_str=False) + assume: + y + claim: + Implies(x, x) + to_prove: + Implies(y, Implies(x, x)) + (True, None) + + >>> r,m = prove(And(x,True),assume=y,verbose=0); r,model_str(m,as_str=False) + (False, [(x, False), (y, True)]) + + >>> r,m = prove(And(x,y),assume=y,verbose=0) + >>> print r + False + >>> print model_str(m,as_str=True) + x = False + y = True + + >>> a,b = Ints('a b') + >>> r,m = prove(a**b == b**a,assume=None,verbose=0) + E: cannot solve ! + >>> r is None and m is None + True + + """ + + if __debug__: + assert not assume or is_expr(assume) + + + to_prove = claim + if assume: + if __debug__: + is_proved,_ = prove(Not(assume)) + + def _f(): + emsg = "Assumption is alway False!" + if verbose >= 2: + emsg = "{}\n{}".format(assume,emsg) + return emsg + + assert is_proved==False, _f() + + to_prove = Implies(assume,to_prove) + + + + if verbose >= 2: + print('assume: ') + print(assume) + print('claim: ') + print(claim) + print('to_prove: ') + print(to_prove) + + f = Not(to_prove) + + models = get_models(f,k=1) + if models is None: #unknown + print('E: cannot solve !') + return None, None + elif models == False: #unsat + return True,None + else: #sat + if __debug__: + assert isinstance(models,list) + + if models: + return False, models[0] #the first counterexample + else: + return False, [] #infinite counterexample,models + + +def get_models(f,k): + """ + Returns the first k models satisfiying f. + If f is not satisfiable, returns False. + If f cannot be solved, returns None + If f is satisfiable, returns the first k models + Note that if f is a tautology, e.g.\ True, then the result is [] + + Based on http://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation + + EXAMPLES: + >>> x, y = Ints('x y') + >>> len(get_models(And(0<=x,x <= 4),k=11)) + 5 + >>> get_models(And(0<=x**y,x <= 1),k=2) is None + True + >>> get_models(And(0<=x,x <= -1),k=2) + False + >>> len(get_models(x+y==7,5)) + 5 + >>> len(get_models(And(x<=5,x>=1),7)) + 5 + >>> get_models(And(x<=0,x>=5),7) + False + + >>> x = Bool('x') + >>> get_models(And(x,Not(x)),k=1) + False + >>> get_models(Implies(x,x),k=1) + [] + >>> get_models(BoolVal(True),k=1) + [] + + + + """ + + if __debug__: + assert is_expr(f) + assert k>=1 + + + + s = Solver() + s.add(f) + + models = [] + i = 0 + while s.check() == sat and i < k: + i = i + 1 + + m = s.model() + + if not m: #if m == [] + break + + models.append(m) + + + #create new constraint to block the current model + block = Not(And([v() == m[v] for v in m])) + s.add(block) + + + if s.check() == unknown: + return None + elif s.check() == unsat and i==0: + return False + else: + return models + +def is_tautology(claim,verbose=0): + """ + >>> is_tautology(Implies(Bool('x'),Bool('x'))) + True + + >>> is_tautology(Implies(Bool('x'),Bool('y'))) + False + + >>> is_tautology(BoolVal(True)) + True + + >>> is_tautology(BoolVal(False)) + False + + """ + return prove(claim=claim,assume=None,verbose=verbose)[0] + + +def is_contradiction(claim,verbose=0): + """ + >>> x,y=Bools('x y') + >>> is_contradiction(BoolVal(False)) + True + + >>> is_contradiction(BoolVal(True)) + False + + >>> is_contradiction(x) + False + + >>> is_contradiction(Implies(x,y)) + False + + >>> is_contradiction(Implies(x,x)) + False + + >>> is_contradiction(And(x,Not(x))) + True + """ + + return prove(claim=Not(claim),assume=None,verbose=verbose)[0] + + +def exact_one_model(f): + """ + return True if f has exactly 1 model, False otherwise. + + EXAMPLES: + + >>> x, y = Ints('x y') + >>> exact_one_model(And(0<=x**y,x <= 0)) + False + + >>> exact_one_model(And(0<=x,x <= 0)) + True + + >>> exact_one_model(And(0<=x,x <= 1)) + False + + >>> exact_one_model(And(0<=x,x <= -1)) + False + """ + + models = get_models(f,k=2) + if isinstance(models,list): + return len(models)==1 + else: + return False + + + +def myBinOp(op,*L): + """ + >>> myAnd(*[Bool('x'),Bool('y')]) + And(x, y) + + >>> myAnd(*[Bool('x'),None]) + x + + >>> myAnd(*[Bool('x')]) + x + + >>> myAnd(*[]) + + >>> myAnd(Bool('x'),Bool('y')) + And(x, y) + + >>> myAnd(*[Bool('x'),Bool('y')]) + And(x, y) + + >>> myAnd([Bool('x'),Bool('y')]) + And(x, y) + + >>> myAnd((Bool('x'),Bool('y'))) + And(x, y) + + >>> myAnd(*[Bool('x'),Bool('y'),True]) + Traceback (most recent call last): + ... + AssertionError + """ + + if __debug__: + assert op == Z3_OP_OR or op == Z3_OP_AND or op == Z3_OP_IMPLIES + + if len(L)==1 and (isinstance(L[0],list) or isinstance(L[0],tuple)): + L = L[0] + + if __debug__: + assert all(not isinstance(l,bool) for l in L) + + L = [l for l in L if is_expr(l)] + if L: + if len(L)==1: + return L[0] + else: + if op == Z3_OP_OR: + return Or(L) + elif op == Z3_OP_AND: + return And(L) + else: #IMPLIES + return Implies(L[0],L[1]) + else: + return None + + +def myAnd(*L): return myBinOp(Z3_OP_AND,*L) +def myOr(*L): return myBinOp(Z3_OP_OR,*L) +def myImplies(a,b):return myBinOp(Z3_OP_IMPLIES,[a,b]) + + + +Iff = lambda f,g: And(Implies(f,g),Implies(g,f)) + + + +def model_str(m,as_str=True): + """ + Returned a 'sorted' model (so that it's easier to see) + The model is sorted by its key, + e.g. if the model is y = 3 , x = 10, then the result is + x = 10, y = 3 + + EXAMPLES: + see doctest exampels from function prove() + + """ + if __debug__: + assert m is None or m == [] or isinstance(m,ModelRef) + + if m : + vs = [(v,m[v]) for v in m] + vs = sorted(vs,key=lambda a,_: str(a)) + if as_str: + return '\n'.join(['{} = {}'.format(k,v) for (k,v) in vs]) + else: + return vs + else: + return str(m) if as_str else m + diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 844a4766c..e299fa31a 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifndef _Z3_API_H_ #define _Z3_API_H_ @@ -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') */ @@ -3868,6 +3897,28 @@ END_MLAPI_EXCLUDE 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); + /** + \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( + __in Z3_context c, __in Z3_func_decl field_access, + __in Z3_ast t, __in Z3_ast value); /** \brief Return arity of relation. @@ -3893,6 +3944,29 @@ END_MLAPI_EXCLUDE Z3_sort Z3_API Z3_get_relation_column(__in Z3_context c, __in 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(__in Z3_context c, __in unsigned num_args, + __in_ecount(num_args) Z3_ast const args[], __in 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(__in Z3_context c, __in unsigned num_args, + __in_ecount(num_args) Z3_ast const args[], __in_ecount(num_args) int coeffs[], + __in int k); + /** \mlonly {3 {L Function Declarations}} \endmlonly */ @@ -4659,6 +4733,13 @@ END_MLAPI_EXCLUDE */ Z3_ast_opt Z3_API Z3_model_get_const_interp(__in Z3_context c, __in Z3_model m, __in 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); + /** \brief Return the interpretation of the function \c f in the model \c m. Return \mlonly [None], \endmlonly \conly \c NULL, @@ -5290,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 @@ -5947,7 +6040,7 @@ END_MLAPI_EXCLUDE /** \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. @@ -6039,6 +6132,197 @@ END_MLAPI_EXCLUDE #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(__in 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(__in Z3_context c,__in 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(__in Z3_context c,__in 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 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( + __in Z3_context c, + __in 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(__in Z3_context c, __in 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(__in Z3_context c,__in Z3_optimize d); + + +#endif + #ifdef CorML4 /*@}*/ @@ -6667,7 +6951,7 @@ END_MLAPI_EXCLUDE def_API('Z3_tactic_apply_ex', APPLY_RESULT, (_in(CONTEXT), _in(TACTIC), _in(GOAL), _in(PARAMS))) */ - Z3_apply_result Z3_API Z3_tactic_apply_ex(Z3_context c, Z3_tactic t, Z3_goal g, Z3_params p); + Z3_apply_result Z3_API Z3_tactic_apply_ex(__in Z3_context c, __in Z3_tactic t, __in Z3_goal g, __in Z3_params p); #ifdef CorML3 /** diff --git a/src/api/z3_fpa.h b/src/api/z3_fpa.h index e1bd67d66..7813142a3 100644 --- a/src/api/z3_fpa.h +++ b/src/api/z3_fpa.h @@ -858,6 +858,20 @@ extern "C" { */ Z3_string Z3_API Z3_fpa_get_numeral_significand_string(__in Z3_context c, __in 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(__in Z3_context c, __in Z3_ast t, __out __uint64 * n); + /** \brief Return the exponent value of a floating-point numeral as a string diff --git a/src/api/z3_macros.h b/src/api/z3_macros.h index 7a0b6857c..cdac41c97 100644 --- a/src/api/z3_macros.h +++ b/src/api/z3_macros.h @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifndef __in #define __in #endif 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..1a5dc8237 100644 --- a/src/api/z3_replayer.h +++ b/src/api/z3_replayer.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/ast/ast.cpp b/src/ast/ast.cpp index 6780145d9..59efb2a89 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); } // ----------------------------------- @@ -1043,6 +1046,13 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_DISTINCT: { func_decl_info info(m_family_id, OP_DISTINCT); info.set_pairwise(); + for (unsigned i = 1; i < arity; i++) { + if (domain[i] != domain[0]) { + std::ostringstream buffer; + buffer << "Sort mismatch between first argument and argument " << (i+1); + throw ast_exception(buffer.str().c_str()); + } + } return m_manager->mk_func_decl(symbol("distinct"), arity, domain, m_bool_sort, info); } default: @@ -2036,7 +2046,13 @@ inline app * ast_manager::mk_app_core(func_decl * decl, expr * arg1, expr * arg2 } 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()); + if (decl->get_arity() != num_args && !decl->is_right_associative() && + !decl->is_left_associative() && !decl->is_chainable()) { + 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()) { @@ -2071,6 +2087,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); @@ -2338,6 +2356,10 @@ quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, unsi num_patterns == 0 ? q->get_no_patterns() : 0); } +app * ast_manager::mk_distinct(unsigned num_args, expr * const * args) { + return mk_app(m_basic_family_id, OP_DISTINCT, num_args, args); +} + app * ast_manager::mk_distinct_expanded(unsigned num_args, expr * const * args) { if (num_args < 2) return mk_true(); diff --git a/src/ast/ast.h b/src/ast/ast.h index 93f456965..19b652033 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -2000,12 +2000,13 @@ public: app * mk_and(expr * arg1, expr * arg2, expr * arg3) { return mk_app(m_basic_family_id, OP_AND, arg1, arg2, arg3); } app * mk_implies(expr * arg1, expr * arg2) { return mk_app(m_basic_family_id, OP_IMPLIES, arg1, arg2); } app * mk_not(expr * n) { return mk_app(m_basic_family_id, OP_NOT, n); } - app * mk_distinct(unsigned num_args, expr * const * args) { return mk_app(m_basic_family_id, OP_DISTINCT, num_args, args); } + app * mk_distinct(unsigned num_args, expr * const * args); app * mk_distinct_expanded(unsigned num_args, expr * const * args); app * mk_true() { return m_true; } 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); 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..e93b0cc58 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.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..64f6b0ab8 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -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_trail.h b/src/ast/ast_trail.h new file mode 100644 index 000000000..94039875e --- /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_util.cpp b/src/ast/ast_util.cpp index d77deca01..79f8f740e 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; 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/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..d5f238650 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.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)); 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..e3bc4dd63 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_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() {} 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/expr_map.h b/src/ast/expr_map.h index 5ab6545f8..3e6f71e86 100644 --- a/src/ast/expr_map.h +++ b/src/ast/expr_map.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/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 69b661589..baba7b701 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); @@ -359,6 +385,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 +865,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 +996,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 +1044,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 +1058,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 +1085,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 +1417,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 +1614,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 +2099,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 +2117,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 +2136,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 +2148,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 +2218,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 +2247,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(); } @@ -2096,79 +2261,88 @@ void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * bool is_int; m_util.au().is_numeral(x, q, is_int); - scoped_mpf v(m_mpf_manager); - m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); + if (q.is_zero()) + return mk_pzero(f, result); + else { + scoped_mpf v(m_mpf_manager); + m_util.fm().set(v, ebits, sbits, mrm, q.to_mpq()); - expr_ref sgn(m), s(m), e(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); - unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v), ebits); - mk_bias(unbiased_exp, e); + 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); + 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, exp); - mk_fp(sgn, e, s, result); + mk_fp(sgn, exp, sig, result); + } } else if (m_util.au().is_numeral(x)) { rational q; bool is_int; m_util.au().is_numeral(x, q, is_int); - expr_ref rm_nta(m), rm_nte(m), rm_tp(m), rm_tn(m), rm_tz(m); - mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_nta); - mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_nte); - mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_tp); - mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_tn); - mk_is_rm(rm, BV_RM_TO_ZERO, rm_tz); + if (m_util.au().is_zero(x)) + mk_pzero(f, result); + else { + expr_ref rm_nta(m), rm_nte(m), rm_tp(m), rm_tn(m), rm_tz(m); + mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_nta); + mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_nte); + mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_tp); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_tn); + mk_is_rm(rm, BV_RM_TO_ZERO, rm_tz); - scoped_mpf v_nta(m_mpf_manager), v_nte(m_mpf_manager), v_tp(m_mpf_manager); - scoped_mpf v_tn(m_mpf_manager), v_tz(m_mpf_manager); - m_util.fm().set(v_nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq()); - m_util.fm().set(v_nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq()); - m_util.fm().set(v_tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq()); - m_util.fm().set(v_tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq()); - m_util.fm().set(v_tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq()); + scoped_mpf v_nta(m_mpf_manager), v_nte(m_mpf_manager), v_tp(m_mpf_manager); + scoped_mpf v_tn(m_mpf_manager), v_tz(m_mpf_manager); + m_util.fm().set(v_nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq()); + m_util.fm().set(v_nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq()); + m_util.fm().set(v_tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq()); + m_util.fm().set(v_tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq()); + m_util.fm().set(v_tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq()); - expr_ref v1(m), v2(m), v3(m), v4(m); + expr_ref v1(m), v2(m), v3(m), v4(m); - expr_ref sgn(m), s(m), e(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); - 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); + 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); + 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, 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); - 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); + sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_nte)) ? 1 : 0, 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, 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); - 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); + sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 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, 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); - 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); + sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tn)) ? 1 : 0, 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, 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); - unbiased_exp = m_bv_util.mk_numeral(m_util.fm().exp(v_tp), ebits); - mk_bias(unbiased_exp, e); - - mk_fp(sgn, e, s, 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); + sgn = m_bv_util.mk_numeral((m_util.fm().sgn(v_tp)) ? 1 : 0, 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, exp); + + 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; - + expr_ref bv0(m), bv1(m), zero(m), two(m); bv0 = bu.mk_numeral(0, 1); bv1 = bu.mk_numeral(1, 1); @@ -2182,9 +2356,9 @@ 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 * 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)); @@ -2209,38 +2383,43 @@ void fpa2bv_converter::mk_to_fp_real_int(func_decl * f, unsigned num, expr * con SASSERT(e.is_int64()); SASSERT(m_mpz_manager.eq(e.to_mpq().denominator(), 1)); - scoped_mpf nte(m_mpf_manager), nta(m_mpf_manager), tp(m_mpf_manager), tn(m_mpf_manager), tz(m_mpf_manager); - m_mpf_manager.set(nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq(), e.to_mpq().numerator()); - m_mpf_manager.set(nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq(), e.to_mpq().numerator()); - m_mpf_manager.set(tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq(), e.to_mpq().numerator()); - m_mpf_manager.set(tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq(), e.to_mpq().numerator()); - m_mpf_manager.set(tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq(), e.to_mpq().numerator()); + if (q.is_zero()) + return mk_pzero(f, result); + else { + scoped_mpf nte(m_mpf_manager), nta(m_mpf_manager), tp(m_mpf_manager), tn(m_mpf_manager), tz(m_mpf_manager); + m_mpf_manager.set(nte, ebits, sbits, MPF_ROUND_NEAREST_TEVEN, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(nta, ebits, sbits, MPF_ROUND_NEAREST_TAWAY, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tp, ebits, sbits, MPF_ROUND_TOWARD_POSITIVE, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tn, ebits, sbits, MPF_ROUND_TOWARD_NEGATIVE, q.to_mpq(), e.to_mpq().numerator()); + m_mpf_manager.set(tz, ebits, sbits, MPF_ROUND_TOWARD_ZERO, q.to_mpq(), e.to_mpq().numerator()); - app_ref a_nte(m), a_nta(m), a_tp(m), a_tn(m), a_tz(m); - a_nte = m_plugin->mk_numeral(nte); - a_nta = m_plugin->mk_numeral(nta); - a_tp = m_plugin->mk_numeral(tp); - a_tn = m_plugin->mk_numeral(tn); - a_tz = m_plugin->mk_numeral(tz); + app_ref a_nte(m), a_nta(m), a_tp(m), a_tn(m), a_tz(m); + a_nte = m_plugin->mk_numeral(nte); + a_nta = m_plugin->mk_numeral(nta); + a_tp = m_plugin->mk_numeral(tp); + a_tn = m_plugin->mk_numeral(tn); + a_tz = m_plugin->mk_numeral(tz); - expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m); - mk_numeral(a_nte->get_decl(), 0, 0, bv_nte); - mk_numeral(a_nta->get_decl(), 0, 0, bv_nta); - mk_numeral(a_tp->get_decl(), 0, 0, bv_tp); - mk_numeral(a_tn->get_decl(), 0, 0, bv_tn); - mk_numeral(a_tz->get_decl(), 0, 0, bv_tz); + expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m); + mk_numeral(a_nte->get_decl(), 0, 0, bv_nte); + mk_numeral(a_nta->get_decl(), 0, 0, bv_nta); + mk_numeral(a_tp->get_decl(), 0, 0, bv_tp); + mk_numeral(a_tn->get_decl(), 0, 0, bv_tn); + mk_numeral(a_tz->get_decl(), 0, 0, bv_tz); - expr_ref c1(m), c2(m), c3(m), c4(m); - c1 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); - c2 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); - c3 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3)); - c4 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3)); + expr_ref c1(m), c2(m), c3(m), c4(m); + c1 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); + c2 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); + c3 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3)); + c4 = m.mk_eq(rm, m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3)); - mk_ite(c1, bv_tn, bv_tz, result); - mk_ite(c2, bv_tp, result, result); - mk_ite(c3, bv_nta, result, result); - mk_ite(c4, bv_nte, result, result); + mk_ite(c1, bv_tn, bv_tz, result); + mk_ite(c2, bv_tp, result, result); + mk_ite(c3, bv_nta, result, result); + mk_ite(c4, bv_nte, result, result); + } } + void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_real", for (unsigned i = 0; i < num; i++) tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); @@ -2367,10 +2546,9 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const mk_pinf(f, pinf); // Special case: x == 0 -> p/n zero - expr_ref c1(m), v1(m), rm_is_to_neg(m); - c1 = is_zero; - mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); - mk_ite(rm_is_to_neg, nzero, pzero, v1); + expr_ref c1(m), v1(m); + c1 = is_zero; + v1 = pzero; // Special case: x != 0 expr_ref is_neg_bit(m), exp_too_large(m), sig_4(m), exp_2(m); @@ -2508,10 +2686,9 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con mk_pinf(f, pinf); // Special case: x == 0 -> p/n zero - expr_ref c1(m), v1(m), rm_is_to_neg(m); + expr_ref c1(m), v1(m); c1 = is_zero; - mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); - mk_ite(rm_is_to_neg, nzero, pzero, v1); + v1 = pzero; // Special case: x != 0 expr_ref exp_too_large(m), sig_4(m), exp_2(m); @@ -2804,9 +2981,8 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg 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); + SASSERT(m_bv_util.get_bv_size(shift_abs) == ebits + 2); + dbg_decouple("fpa2bv_to_sbv_shift", shift); // sig is of the form +- [1].[sig][r][g][s] ... and at least bv_sz + 3 long // [1][ ... sig ... ][r][g][ ... s ...] @@ -2815,34 +2991,48 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg 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); + dbg_decouple("fpa2bv_to_sbv_shift_abs", shift_abs); 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 huge_sig(m), huge_shift(m), huge_shifted_sig(m); + huge_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, sig_sz)); + huge_shift = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, sig_sz), shift_abs); + huge_shifted_sig = m_bv_util.mk_bv_lshr(huge_sig, huge_shift); + dbg_decouple("fpa2bv_to_sbv_huge_shifted_sig", huge_shifted_sig); + SASSERT(m_bv_util.get_bv_size(huge_shifted_sig) == 2 * sig_sz); + + expr_ref upper_hss(m), lower_hss(m); + upper_hss = m_bv_util.mk_extract(2 * sig_sz - 1, sig_sz + 1, huge_shifted_sig); + lower_hss = m_bv_util.mk_extract(sig_sz, 0, huge_shifted_sig); + SASSERT(m_bv_util.get_bv_size(upper_hss) == sig_sz - 1); + SASSERT(m_bv_util.get_bv_size(lower_hss) == sig_sz + 1); + dbg_decouple("fpa2bv_to_sbv_upper_hss", upper_hss); + dbg_decouple("fpa2bv_to_sbv_lower_hss", lower_hss); 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); + last = m_bv_util.mk_extract(1, 1, upper_hss); + round = m_bv_util.mk_extract(0, 0, upper_hss); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, lower_hss.get()); dbg_decouple("fpa2bv_to_sbv_last", last); dbg_decouple("fpa2bv_to_sbv_round", round); dbg_decouple("fpa2bv_to_sbv_sticky", sticky); + expr_ref upper_hss_w_sticky(m); + upper_hss_w_sticky = m_bv_util.mk_concat(upper_hss, sticky); + dbg_decouple("fpa2bv_to_sbv_upper_hss_w_sticky", upper_hss_w_sticky); + SASSERT(m_bv_util.get_bv_size(upper_hss_w_sticky) == sig_sz); + 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)); + unrounded_sig = m_bv_util.mk_extract(sig_sz - 1, sig_sz - bv_sz - 1, upper_hss_w_sticky); + inc = m_bv_util.mk_zero_extend(bv_sz, 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); @@ -3029,14 +3219,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); } @@ -3182,7 +3374,7 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref m_simp.mk_ite(m.mk_or(is_normal, is_sig_zero), zero_e, lz_d, lz); dbg_decouple("fpa2bv_unpack_lz", lz); - expr_ref shift(m); + expr_ref shift(m); m_simp.mk_ite(is_sig_zero, zero_e, lz, shift); dbg_decouple("fpa2bv_unpack_shift", shift); SASSERT(is_well_sorted(m, is_sig_zero)); @@ -3197,7 +3389,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); @@ -3258,6 +3450,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 }; @@ -3295,6 +3498,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; } diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index fa797b610..b0881a364 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -80,6 +80,7 @@ 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); @@ -92,7 +93,7 @@ public: 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); @@ -151,6 +152,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); diff --git a/src/ast/fpa/fpa2bv_rewriter.h b/src/ast/fpa/fpa2bv_rewriter.h index ed885a4cc..fa88c227c 100644 --- a/src/ast/fpa/fpa2bv_rewriter.h +++ b/src/ast/fpa/fpa2bv_rewriter.h @@ -103,8 +103,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 +111,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()) { diff --git a/src/ast/fpa_decl_plugin.cpp b/src/ast/fpa_decl_plugin.cpp index 48b7adb8b..0b28c4277 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, @@ -879,12 +879,20 @@ void fpa_decl_plugin::get_sort_names(svector & sort_names, symbol } expr * fpa_decl_plugin::get_some_value(sort * s) { - SASSERT(s->is_sort_of(m_family_id, FLOATING_POINT_SORT)); - mpf tmp; - m_fm.mk_nan(s->get_parameter(0).get_int(), s->get_parameter(1).get_int(), tmp); - expr * res = this->mk_numeral(tmp); - m_fm.del(tmp); - return res; + if (s->is_sort_of(m_family_id, FLOATING_POINT_SORT)) { + mpf tmp; + m_fm.mk_nan(s->get_parameter(0).get_int(), s->get_parameter(1).get_int(), tmp); + expr * res = mk_numeral(tmp); + m_fm.del(tmp); + return res; + } + else if (s->is_sort_of(m_family_id, ROUNDING_MODE_SORT)) { + func_decl * f = mk_rm_const_decl(OP_FPA_RM_TOWARD_ZERO, 0, 0, 0, 0, s); + return m_manager->mk_const(f); + } + + UNREACHABLE(); + return 0; } bool fpa_decl_plugin::is_value(app * e) const { 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/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_util.cpp b/src/ast/macros/macro_util.cpp index bd21aa1ac..de55de632 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -489,7 +489,6 @@ void macro_util::normalize_expr(app * head, expr * t, expr_ref & norm_t) const { tout << "#" << i << " -> " << mk_pp(var_mapping[i], m_manager) << "\n"; }); subst(t, var_mapping.size(), var_mapping.c_ptr(), norm_t); - SASSERT(is_well_sorted(m_manager, norm_t)); } else { norm_t = t; diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 8e0065071..c3ea69e77 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -137,7 +137,6 @@ class skolemizer { } } s(body, substitution.size(), substitution.c_ptr(), r); - SASSERT(is_well_sorted(m(), r)); p = 0; if (m().proofs_enabled()) { if (q->is_forall()) 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..152f7f459 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.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); }; diff --git a/src/ast/pb_decl_plugin.cpp b/src/ast/pb_decl_plugin.cpp new file mode 100644 index 000000000..36103c4f0 --- /dev/null +++ b/src/ast/pb_decl_plugin.cpp @@ -0,0 +1,282 @@ +/*++ +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; +} diff --git a/src/ast/pb_decl_plugin.h b/src/ast/pb_decl_plugin.h new file mode 100644 index 000000000..0bc8eab17 --- /dev/null +++ b/src/ast/pb_decl_plugin.h @@ -0,0 +1,123 @@ +/*++ +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 + 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; + 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; + + +private: + rational to_rational(parameter const& p) const; +}; + + + + +#endif /* _PB_DECL_PLUGIN_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/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/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..a362c235b 100644 --- a/src/ast/rewriter/ast_counter.h +++ b/src/ast/rewriter/ast_counter.h @@ -27,16 +27,16 @@ Revision History: #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_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/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/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..1f21d35e9 100644 --- a/src/ast/rewriter/expr_safe_replace.h +++ b/src/ast/rewriter/expr_safe_replace.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__ */ diff --git a/src/ast/rewriter/fpa_rewriter.cpp b/src/ast/rewriter/fpa_rewriter.cpp index 6aee683ff..07a39bcae 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; @@ -143,9 +143,9 @@ br_status fpa_rewriter::mk_to_sbv_unspecified(func_decl * f, expr_ref & result) 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 +243,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 +411,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 +441,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; } @@ -472,7 +492,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 +591,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 +602,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 +613,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; diff --git a/src/ast/rewriter/fpa_rewriter.h b/src/ast/rewriter/fpa_rewriter.h index 2c76fad6a..2da839718 100644 --- a/src/ast/rewriter/fpa_rewriter.h +++ b/src/ast/rewriter/fpa_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); diff --git a/src/ast/rewriter/pb_rewriter.cpp b/src/ast/rewriter/pb_rewriter.cpp new file mode 100644 index 000000000..ce6e294c7 --- /dev/null +++ b/src/ast/rewriter/pb_rewriter.cpp @@ -0,0 +1,275 @@ +/*++ +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_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: + m_args.reset(); + m_coeffs.reset(); + for (unsigned i = 0; i < vec.size(); ++i) { + m_args.push_back(vec[i].first); + m_coeffs.push_back(vec[i].second); + } + if (is_eq) { + result = m_util.mk_eq(vec.size(), m_coeffs.c_ptr(), m_args.c_ptr(), k); + } + else { + result = m_util.mk_ge(vec.size(), 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..4c9aaaaf1 --- /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..38efd2097 --- /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_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/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index 92f06679d..be3bfbd0a 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; 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/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..7db756d30 100644 --- a/src/ast/rewriter/var_subst.h +++ b/src/ast/rewriter/var_subst.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/shared_occs.h b/src/ast/shared_occs.h index e135fea84..b522f4a4d 100644 --- a/src/ast/shared_occs.h +++ b/src/ast/shared_occs.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/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/base_simplifier.h b/src/ast/simplifier/base_simplifier.h index 4afe5c9d7..26bb43838 100644 --- a/src/ast/simplifier/base_simplifier.h +++ b/src/ast/simplifier/base_simplifier.h @@ -20,21 +20,32 @@ Notes: #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_ */ 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/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/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/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/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/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/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..78e28bea9 100644 --- a/src/ast/simplifier/simplifier.h +++ b/src/ast/simplifier/simplifier.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/static_features.h b/src/ast/static_features.h index 4bdcd6f05..c07667325 100644 --- a/src/ast/static_features.h +++ b/src/ast/static_features.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]++; } 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..07723a8e4 100644 --- a/src/ast/substitution/substitution_tree.h +++ b/src/ast/substitution/substitution_tree.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(); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 0c60d876b..c8d5e8cab 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,7 @@ Notes: #include"for_each_expr.h" #include"scoped_timer.h" #include"interpolant_cmds.h" +#include"model_smt2_pp.h" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { @@ -336,10 +338,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 +359,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 +501,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 +529,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 +549,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 +562,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 +619,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 +687,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 +755,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 +1188,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 +1204,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 +1272,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 +1369,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 +1387,57 @@ 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; + 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); + 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); + if (mdl) { + regular_stream() << "(model " << std::endl; + model_smt2_pp(regular_stream(), *this, *(mdl.get()), 2); + regular_stream() << ")" << std::endl; + } + } + 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; try { r = m_solver->check_sat(num_assumptions, assumptions); } @@ -1383,15 +1448,17 @@ 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(); + } void cmd_context::display_sat_result(lbool r) { @@ -1568,6 +1635,9 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) { 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..795b3a65a 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.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,8 @@ 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 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/context_params.cpp b/src/cmd_context/context_params.cpp index 13f6f5be3..4ba26aceb 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -93,13 +93,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()); } } diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index f41f175ea..3add76030 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)) 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..dc509d809 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -727,7 +727,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..34340cdce --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -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..810afb103 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 @@ -420,12 +420,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 +448,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 +477,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 +506,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/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 5a7f71987..268085090 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); 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..2b4100e5e 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; @@ -2784,7 +2788,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 +2809,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..3620f0ad1 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); @@ -1702,14 +1733,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 +1915,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 +1937,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 +2005,16 @@ public: res = make(commute,pf,comm_equiv); break; } + 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"); @@ -2003,10 +2053,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 +2077,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/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 8f4c55392..638c83642 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -542,6 +542,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); } 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/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/simplex/network_flow.h b/src/math/simplex/network_flow.h new file mode 100644 index 000000000..e0b415c03 --- /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..93e1ded9a --- /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..d1d6320fc --- /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..3c1a90a7a --- /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..8c0357691 --- /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..64dcd8d84 --- /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/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/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_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..21c91b3cb --- /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/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..87b2f186b --- /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..cc0e28e2a 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: 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..3555e5bdc 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -210,14 +210,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 +226,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 +272,39 @@ 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_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 +325,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 +352,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 +464,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 +550,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 +687,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 +728,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 +792,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 +821,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 +849,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 +860,7 @@ namespace datalog { case QBMC_ENGINE: case TAB_ENGINE: case CLP_ENGINE: + case DDNF_ENGINE: flush_add_rules(); break; case DUALITY_ENGINE: @@ -1009,6 +888,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 +945,10 @@ namespace datalog { if (m_engine) { m_engine->collect_statistics(st); } + unsigned long long max_mem = memory::get_max_used_memory(); + unsigned long long mem = memory::get_allocation_size(); + st.update("max memory", static_cast(max_mem)/(1024.0*1024.0)); + st.update("memory", static_cast(mem)/(1024.0*1024.0)); } @@ -1107,25 +991,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 +1020,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,10 +1051,7 @@ 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; @@ -1158,21 +1059,22 @@ namespace datalog { 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(queries.size(), queries.c_ptr(), visited, visitor, fresh_names); func_decl_set funcs; func_decl_set::iterator it = visitor.funcs().begin(); func_decl_set::iterator end = visitor.funcs().end(); @@ -1258,22 +1160,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..9f94bd869 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -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,32 @@ 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; void register_finite_sort(sort * s, sort_kind k); @@ -286,12 +293,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 +375,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 +429,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 +476,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 +575,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); diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index eaeebe979..21a07fcb0 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.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..eabe1d551 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())); + qe::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) { @@ -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..468b9f88c 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.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; diff --git a/src/muz/base/dl_rule_set.cpp b/src/muz/base/dl_rule_set.cpp index ad3b512a3..555b592ef 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..a7d09b099 100644 --- a/src/muz/base/dl_rule_set.h +++ b/src/muz/base/dl_rule_set.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); 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..513add584 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.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(); diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 832e3329e..33d1daf57 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -2,83 +2,147 @@ 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"), + ('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'), )) diff --git a/src/muz/base/hnf.cpp b/src/muz/base/hnf.cpp index 9d6f3c1ab..a0dbe9518 100644 --- a/src/muz/base/hnf.cpp +++ b/src/muz/base/hnf.cpp @@ -58,6 +58,19 @@ Notes: #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 +86,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 +103,41 @@ 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)) { + 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 +210,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; @@ -348,13 +381,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..4d3edcb68 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 --*/ +/*-- + 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_ */ diff --git a/src/muz/base/proof_utils.cpp b/src/muz/base/proof_utils.cpp index 87ecf9985..4bfbf3365 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)) { diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp new file mode 100644 index 000000000..455e02e45 --- /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)) { + 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, 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..18ecd2ee8 --- /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..57e4ba8cb 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; @@ -1442,7 +1443,7 @@ namespace datalog { 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); @@ -1498,7 +1499,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/clp/clp_context.cpp b/src/muz/clp/clp_context.cpp index 11f6ae1db..d15172e22 100644 --- a/src/muz/clp/clp_context.cpp +++ b/src/muz/clp/clp_context.cpp @@ -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/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..79c79ed9d --- /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/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 830f6d078..c6f594d4e 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,27 +91,17 @@ 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 = err == 0; #else m_file = fopen(fname, "rb"); #endif @@ -116,7 +109,8 @@ public: ~line_reader() { fclose(m_file); } -#endif + + bool operator()() { return m_ok; } /** \brief Retrieve next line from the stream. @@ -170,6 +164,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 +226,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 +246,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 +266,7 @@ public: dlexer(): m_input(0), + m_reader(0), m_prev_char(0), m_curr_char(0), m_line(1), @@ -242,8 +275,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 +313,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 +468,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 +486,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 +495,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 +513,7 @@ public: reset(); std::string s(string); std::istringstream is(s); - return parse_stream(is); + return parse_stream(&is, 0); } protected: @@ -486,7 +521,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 +541,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); @@ -741,7 +775,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 +789,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) { @@ -963,10 +1001,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 +1013,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 +1029,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 +1058,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)) { @@ -1075,7 +1111,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 +1125,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; } @@ -1278,7 +1311,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; diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index a0af94953..66f1c6d38 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); 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/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index 0c094c277..6032b3f02 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -190,7 +190,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 +316,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 +334,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/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index a1f3af401..134c0746d 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"; } ); @@ -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) { @@ -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)); } @@ -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,7 +2172,7 @@ 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); @@ -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..070a80ca4 100644 --- a/src/muz/pdr/pdr_context.h +++ b/src/muz/pdr/pdr_context.h @@ -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..b75be70c1 100644 --- a/src/muz/pdr/pdr_dl_interface.cpp +++ b/src/muz/pdr/pdr_dl_interface.cpp @@ -105,7 +105,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 +122,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) { @@ -176,7 +176,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_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index 7c38bf86f..5946ccb2a 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), diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index 7c2557260..8b6f6a4c6 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -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_manager.cpp b/src/muz/pdr/pdr_manager.cpp index bda54dbd7..c029d1b16 100644 --- a/src/muz/pdr/pdr_manager.cpp +++ b/src/muz/pdr/pdr_manager.cpp @@ -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"; diff --git a/src/muz/pdr/pdr_manager.h b/src/muz/pdr/pdr_manager.h index 0e8e890e8..f892e4d97 100644 --- a/src/muz/pdr/pdr_manager.h +++ b/src/muz/pdr/pdr_manager.h @@ -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..9ba976254 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 @@ -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..a44ac7e5a 100644 --- a/src/muz/pdr/pdr_prop_solver.h +++ b/src/muz/pdr/pdr_prop_solver.h @@ -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_smt_context_manager.h b/src/muz/pdr/pdr_smt_context_manager.h index 4775dc58f..625428042 100644 --- a/src/muz/pdr/pdr_smt_context_manager.h +++ b/src/muz/pdr/pdr_smt_context_manager.h @@ -21,7 +21,6 @@ Revision History: #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_util.cpp b/src/muz/pdr/pdr_util.cpp index f122f1e2c..18e5b680d 100644 --- a/src/muz/pdr/pdr_util.cpp +++ b/src/muz/pdr/pdr_util.cpp @@ -90,879 +90,6 @@ 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); @@ -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..29b640d74 100644 --- a/src/muz/pdr/pdr_util.h +++ b/src/muz/pdr/pdr_util.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/check_relation.cpp b/src/muz/rel/check_relation.cpp new file mode 100644 index 000000000..60ca6c0be --- /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_VERBOSE(3, verbose_stream() << "NOT verified " << res << "\n"; + verbose_stream() << mk_pp(fml1, m) << "\n"; + verbose_stream() << mk_pp(fml2, m) << "\n"; + verbose_stream().flush(); + ); + throw 0; + } + } + + 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..8000a2c72 --- /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..6ab1b2a96 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -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. 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..a9e37a8a3 100644 --- a/src/muz/rel/dl_compiler.h +++ b/src/muz/rel/dl_compiler.h @@ -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); diff --git a/src/muz/rel/dl_instruction.cpp b/src/muz/rel/dl_instruction.cpp index a702c27ce..f8145b922 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; @@ -367,7 +403,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 +414,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 +436,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; } @@ -417,7 +455,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 +465,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 +485,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; } @@ -464,12 +504,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 +533,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); @@ -508,14 +550,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 +585,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); @@ -563,14 +607,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 +650,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 +715,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 +731,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 +759,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 +787,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,10 +826,12 @@ 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); @@ -796,15 +846,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 +884,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("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,7 +956,8 @@ 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)) { @@ -857,14 +971,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 +1007,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 +1028,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 +1065,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 +1098,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 +1125,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 +1147,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 +1202,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 +1217,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 +1234,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..a02346b99 100644 --- a/src/muz/rel/dl_instruction.h +++ b/src/muz/rel/dl_instruction.h @@ -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,10 +357,12 @@ 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(); } }; 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_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_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_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..f2efd9678 100644 --- a/src/muz/rel/dl_product_relation.h +++ b/src/muz/rel/dl_product_relation.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..f91b7496a 100644 --- a/src/muz/rel/dl_relation_manager.h +++ b/src/muz/rel/dl_relation_manager.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. 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_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_vector_relation.h b/src/muz/rel/dl_vector_relation.h index 4d0917359..c349c6e19 100644 --- a/src/muz/rel/dl_vector_relation.h +++ b/src/muz/rel/dl_vector_relation.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..6ffefaf88 --- /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..6b9126161 100644 --- a/src/muz/rel/karr_relation.cpp +++ b/src/muz/rel/karr_relation.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "karr_relation.h" #include "bool_rewriter.h" diff --git a/src/muz/rel/rel_context.cpp b/src/muz/rel/rel_context.cpp index 4e219b2f7..d2a2f0181 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) { @@ -243,11 +255,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 +290,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 +333,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(); @@ -359,7 +390,7 @@ namespace datalog { 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; } @@ -433,7 +464,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 +494,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 +522,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 +551,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 +586,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 +632,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..e445c926f 100644 --- a/src/muz/rel/rel_context.h +++ b/src/muz/rel/rel_context.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. 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..7d20d035c --- /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..6d69550ef --- /dev/null +++ b/src/muz/rel/udoc_relation.cpp @@ -0,0 +1,1264 @@ +/*++ +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); + qe::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); + qe::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& d, bit_vector const& discard_cols) const { + d.reset(dm); + d.push_back(dm.allocateX()); + apply_guard(g, d, discard_cols); + } + void udoc_relation::apply_guard(expr* g, udoc& result, bit_vector const& discard_cols) const { + // 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); + t.compile_guard(guard, m_udoc, 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) + 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..40f2222fc --- /dev/null +++ b/src/muz/rel/udoc_relation.h @@ -0,0 +1,154 @@ +/*++ +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, bit_vector const& discard_cols) 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..9b005cb54 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -216,11 +216,13 @@ namespace tb { } 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 { @@ -763,7 +765,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 +1109,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 +1129,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/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 641d40779..bc3b0cc38 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -266,11 +266,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 +280,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 +289,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 +297,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_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_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_interp_tail_simplifier.cpp b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp index b01b4326c..ca3042ff5 100644 --- a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp @@ -491,9 +491,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; } diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index fcb8d7b85..95d3e8e73 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -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..37a5b8a6c 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.h +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.h @@ -26,8 +26,9 @@ Revision History: #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; 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_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..03a52c997 100644 --- a/src/muz/transforms/dl_mk_separate_negated_tails.h +++ b/src/muz/transforms/dl_mk_separate_negated_tails.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..2a49d534e 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -120,7 +120,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 +238,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()); @@ -676,10 +676,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 +773,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 +813,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_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_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_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/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..bd9c3d344 --- /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..7c6369f6a --- /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..58de8c518 --- /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/inc_sat_solver.cpp b/src/opt/inc_sat_solver.cpp new file mode 100644 index 000000000..fb8172373 --- /dev/null +++ b/src/opt/inc_sat_solver.cpp @@ -0,0 +1,438 @@ +/*++ +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 "nnf_tactic.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" + +// 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; + 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; + expr_ref_vector m_soft; + vector m_weights; + bool m_soft_assumptions; + + + typedef obj_map dep2asm_t; +public: + inc_sat_solver(ast_manager& m, params_ref const& p): + m(m), m_solver(p,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_num_scopes(0), + m_dep_core(m), + m_soft(m), + m_soft_assumptions(false) { + 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), + //mk_simplify_tactic(m), + //mk_propagate_values_tactic(m), + using_params(mk_simplify_tactic(m), simp2_p), + mk_max_bv_sharing_tactic(m), + mk_bit_blaster_tactic(m), + 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) { + 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(num_assumptions, assumptions, dep2asm); + if (r != l_true) return r; + extract_assumptions(dep2asm, m_asms); + + r = initialize_soft_constraints(); + if (r != l_true) return r; + + r = m_solver.check(m_asms.size(), m_asms.c_ptr()); + switch (r) { + case l_true: + if (num_assumptions > 0) { + check_assumptions(dep2asm); + } + break; + case l_false: + // TBD: expr_dependency core is not accounted for. + if (num_assumptions > 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); + } + virtual void pop(unsigned n) { + if (n < m_num_scopes) { // allow inc_sat_solver to + n = m_num_scopes; // take over for another solver. + } + 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("opt", 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_soft_assumptions = m_params.get_bool("soft_assumptions", false); + 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]; + } + void set_soft(unsigned sz, expr*const* soft, rational const* weights) { + m_soft.reset(); + m_weights.reset(); + m_soft.append(sz, soft); + m_weights.append(sz, weights); + } + +private: + + lbool initialize_soft_constraints() { + dep2asm_t dep2asm; + if (m_soft.empty()) { + return l_true; + } + expr_ref_vector soft(m_soft); + for (unsigned i = 0; i < soft.size(); ++i) { + expr* e = soft[i].get(), *e1; + if (is_uninterp_const(e) || (m.is_not(e, e1) && is_uninterp_const(e1))) { + continue; + } + expr_ref asum(m), fml(m); + asum = m.mk_fresh_const("soft", m.mk_bool_sort()); + fml = m.mk_iff(asum, e); + m_fmls.push_back(fml); + soft[i] = asum; + } + m_soft.reset(); + lbool r = internalize_formulas(); + if (r != l_true) return r; + r = internalize_assumptions(soft.size(), soft.c_ptr(), dep2asm); + if (r != l_true) return r; + sat::literal_vector lits; + svector weights; + sat::literal lit; + for (unsigned i = 0; i < soft.size(); ++i) { + weights.push_back(m_weights[i].get_double()); + expr* s = soft[i].get(); + if (!dep2asm.find(s, lit)) { + IF_VERBOSE(0, + verbose_stream() << "not found: " << mk_pp(s, m) << "\n"; + dep2asm_t::iterator it = dep2asm.begin(); + dep2asm_t::iterator end = dep2asm.end(); + for (; it != end; ++it) { + verbose_stream() << mk_pp(it->m_key, m) << " " << it->m_value << "\n"; + } + UNREACHABLE();); + } + lits.push_back(lit); + } + m_solver.initialize_soft(lits.size(), lits.c_ptr(), weights.c_ptr()); + return r; + } + + lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm) { + m_mc2.reset(); + m_pc.reset(); + m_dep_core.reset(); + m_subgoals.reset(); + SASSERT(g->models_enabled()); + SASSERT(!g->proofs_enabled()); + TRACE("opt", 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; + } + m_mc = concat(m_mc.get(), m_mc2.get()); + 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; + } + g = m_subgoals[0]; + TRACE("opt", 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])); + } + return internalize_goal(g, dep2asm); + } + + 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(dep2asm_t& dep2asm, sat::literal_vector& asms) { + asms.reset(); + dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); + for (; it != end; ++it) { + asms.push_back(it->m_value); + } + //IF_VERBOSE(0, verbose_stream() << asms << "\n";); + } + + void extract_core(dep2asm_t& dep2asm) { + u_map asm2dep; + dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); + for (; it != end; ++it) { + asm2dep.insert(it->m_value.index(), it->m_key); + } + sat::literal_vector const& core = m_solver.get_core(); + TRACE("opt", + 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 (!m_soft_assumptions && 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_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("opt", !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& p) { + return alloc(inc_sat_solver, m, p); +} + +void set_soft_inc_sat(solver* _s, unsigned sz, expr*const* soft, rational const* weights) { + inc_sat_solver* s = dynamic_cast(_s); + s->set_soft(sz, soft, weights); +} diff --git a/src/opt/inc_sat_solver.h b/src/opt/inc_sat_solver.h new file mode 100644 index 000000000..8bb4c288a --- /dev/null +++ b/src/opt/inc_sat_solver.h @@ -0,0 +1,29 @@ +/*++ +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& p); + +void set_soft_inc_sat(solver* s, unsigned sz, expr*const* soft, rational const* weights); + +#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..ed59bf625 --- /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..ddd238156 --- /dev/null +++ b/src/opt/maxres.cpp @@ -0,0 +1,799 @@ +/*++ +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; + 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. + + 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_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) + { + } + + 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(3, 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); + } + + lbool mus_solver() { + init(); + init_local(); + trace_bounds("maxres"); + 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_true) return is_sat; + break; + case l_undef: + return l_undef; + default: + break; + } + } + trace_bounds("maxres"); + return l_true; + } + + lbool primal_dual_solver() { + init(); + init_local(); + set_soft_assumptions(); + lbool is_sat = l_true; + trace_bounds("max_res"); + exprs cs; + while (m_lower < m_upper) { +#if 0 + expr_ref_vector asms(m_asms); + sort_assumptions(asms); + is_sat = s().check_sat(asms.size(), asms.c_ptr()); +#else + is_sat = check_sat_hill_climb(m_asms); +#endif + if (m_cancel) { + return l_undef; + } + switch (is_sat) { + case l_true: + get_current_correction_set(cs); + IF_VERBOSE(2, display_vec(verbose_stream() << "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_true) return is_sat; + break; + case l_undef: + return l_undef; + default: + break; + } + } + trace_bounds("maxres"); + 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 = s().check_sat(index, asms.c_ptr()); + } + } + else { + is_sat = s().check_sat(asms.size(), asms.c_ptr()); + } + return is_sat; + } + + void found_optimum() { + IF_VERBOSE(1, verbose_stream() << "found optimum\n";); + s().get_model(m_model); + DEBUG_CODE( + for (unsigned i = 0; i < m_asms.size(); ++i) { + SASSERT(is_true(m_asms[i].get())); + }); + 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) { + break; + } + if (core.empty()) { + 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) { + ++m_stats.m_num_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) { + 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";); + } + + 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 process_unsat(exprs const& core) { + expr_ref fml(m); + remove_core(core); + SASSERT(!core.empty()); + rational w = split_core(core); + TRACE("opt", display_vec(tout << "minimized 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; + trace_bounds("maxres"); + } + + 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); + } + + void sls() { + vector ws; + for (unsigned i = 0; i < m_asms.size(); ++i) { + ws.push_back(get_weight(m_asms[i].get())); + } + enable_sls(m_asms, ws); + } + + 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); + d = dd; + } + asum = mk_fresh_bool("a"); + cls = m.mk_or(b_i1, d); + fml = m.mk_implies(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); + 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_or(m_B.size(), m_B.c_ptr()); + s().assert_expr(fml); + } + + void update_assignment(model* mdl) { + 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]); + } + m_upper = upper; + DEBUG_CODE(verify_assignment();); + trace_bounds("maxres"); + + 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); + } + + 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_wmax = _p.maxres_wmax(); + } + + 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(); + } + + 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..bb280cb29 --- /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..d5ddb6623 --- /dev/null +++ b/src/opt/maxsls.cpp @@ -0,0 +1,62 @@ +/*++ +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" + + +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(); + set_enable_sls(true); + enable_sls(m_soft, m_weights); + lbool is_sat = s().check_sat(0, 0); + 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; + } + + }; + + 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..f344c1564 --- /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..92dac9474 --- /dev/null +++ b/src/opt/maxsmt.cpp @@ -0,0 +1,323 @@ +/*++ +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); + s().updt_params(p); + } + + void maxsmt_solver_base::enable_sls(expr_ref_vector const& soft, vector const& ws) { + m_c.enable_sls(soft, ws); + } + + void maxsmt_solver_base::set_enable_sls(bool f) { + m_c.set_enable_sls(f); + } + + void maxsmt_solver_base::set_soft_assumptions() { + m_c.set_soft_assumptions(); + } + + 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..a2881ad21 --- /dev/null +++ b/src/opt/maxsmt.h @@ -0,0 +1,156 @@ +/*++ +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(expr_ref_vector const& soft, weights_t& ws); + void set_enable_sls(bool f); + void set_soft_assumptions(); + 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..02d218e4d --- /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..566f4ec0b --- /dev/null +++ b/src/opt/mus.cpp @@ -0,0 +1,235 @@ +/*++ +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..12cd8b0b2 --- /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..01324fc67 --- /dev/null +++ b/src/opt/opt_cmds.cpp @@ -0,0 +1,341 @@ +/*++ +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_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) { + } + +}; + + +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("dweight", CPK_DECIMAL, "(default: 1.0) penalty as double 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 = ps().get_rat(symbol("dweight"), 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) { + } +}; + +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()); + } +}; + + + +void install_opt_cmds(cmd_context & ctx) { + ctx.insert(alloc(assert_weighted_cmd)); + ctx.insert(alloc(assert_soft_cmd)); + ctx.insert(alloc(min_maximize_cmd, true)); + ctx.insert(alloc(min_maximize_cmd, false)); + ctx.insert(alloc(optimize_cmd)); +} + diff --git a/src/opt/opt_cmds.h b/src/opt/opt_cmds.h new file mode 100644 index 000000000..048871987 --- /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..6fb4ef542 --- /dev/null +++ b/src/opt/opt_context.cpp @@ -0,0 +1,1373 @@ +/*++ +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 "elim_term_ite_tactic.h" +#include "tactical.h" +#include "model_smt2_pp.h" +#include "card2bv_tactic.h" +#include "nnf_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" + +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_enable_sls(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()); + } + + 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_true) { + m_model = 0; + return is_sat; + } + IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n";); + s.get_model(m_model); + 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; + } + } + + lbool context::execute_lex() { + lbool r = l_true; + 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(), !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; + } + + 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")) { + 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::set_soft_assumptions() { + if (m_sat_solver.get()) { + m_params.set_bool("soft_assumptions", true); + m_sat_solver->updt_params(m_params); + } + } + + void context::enable_sls(expr_ref_vector const& soft, vector const& weights) { + SASSERT(soft.size() == weights.size()); + if (m_sat_solver.get()) { + set_soft_inc_sat(m_sat_solver.get(), soft.size(), soft.c_ptr(), weights.c_ptr()); + } + if (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), + mk_elim_term_ite_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_elim_term_ite_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())); + } + else { + set_simplify(tac0.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)) { + m_objectives[index].m_term = tr; + } + else if (is_minimize(fml, tr, orig_term, index)) { + m_objectives[index].m_term = tr; + m_objectives[index].m_adjust_value.set_negate(true); + } + else { + m_hard_constraints.push_back(fml); + } + } + } + + /** + 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) { + 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 { + q = m.mk_fresh_const("obj", m.mk_bool_sort()); + terms[i] = q; + m_hard_constraints.push_back(m.mk_iff(p, q)); + if (!fm) fm = alloc(filter_model_converter, m); + fm->insert(q->get_decl()); + } + } + 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(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); + } + unsigned long long max_mem = memory::get_max_used_memory(); + unsigned long long mem = memory::get_allocation_size(); + stats.update("memory", static_cast(mem)/static_cast(1024*1024)); + stats.update("max memory", static_cast(max_mem)/static_cast(1024*1024)); + } + + 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(); + } + + typedef obj_hashtable func_decl_set; + + struct context::free_func_visitor { + ast_manager& m; + func_decl_set m_funcs; + obj_hashtable m_sorts; + expr_mark m_visited; + public: + free_func_visitor(ast_manager& m): m(m) {} + void operator()(var * n) { } + void operator()(app * n) { + if (n->get_family_id() == null_family_id) { + 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; } + + void collect(expr* e) { + for_each_expr(*this, m_visited, e); + } + }; + + std::string context::to_string() const { + smt2_pp_environment_dbg env(m); + ast_smt_pp ll_smt2_pp(m); + free_func_visitor visitor(m); + std::ostringstream out; +#define PP(_e_) ast_smt2_pp(out, _e_, env); +#define PPE(_e_) if (m_pp_neat) ast_smt2_pp(out, _e_, env); else ll_smt2_pp.display_expr_smt2(out, _e_); + for (unsigned i = 0; i < m_scoped_state.m_hard.size(); ++i) { + visitor.collect(m_scoped_state.m_hard[i]); + } + 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: + for (unsigned j = 0; j < obj.m_terms.size(); ++j) { + visitor.collect(obj.m_terms[j]); + } + break; + default: + UNREACHABLE(); + break; + } + } + + obj_hashtable::iterator sit = visitor.sorts().begin(); + obj_hashtable::iterator send = visitor.sorts().end(); + for (; sit != send; ++sit) { + PP(*sit); + } + func_decl_set::iterator it = visitor.funcs().begin(); + func_decl_set::iterator end = visitor.funcs().end(); + for (; it != end; ++it) { + PP(*it); + out << "\n"; + } + for (unsigned i = 0; i < m_scoped_state.m_hard.size(); ++i) { + out << "(assert "; + PPE(m_scoped_state.m_hard[i]); + out << ")\n"; + } + 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..eabc2550f --- /dev/null +++ b/src/opt/opt_context.h @@ -0,0 +1,295 @@ +/*++ +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(expr_ref_vector const& soft, weights_t& weights) = 0; // stochastic local search + virtual void set_enable_sls(bool f) = 0; // overwrite whether SLS is enabled. + virtual void set_soft_assumptions() = 0; // configure SAT solver to skip assumptions assigned by unit-propagation + 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) + }; + + /** + \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 { + struct free_func_visitor; + 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 { return std::string("unknown"); } + + 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(expr_ref_vector const& soft, weights_t& weights); + virtual void set_enable_sls(bool f) { m_enable_sls = f; } + virtual void set_soft_assumptions(); + 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(); + 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); + 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(); + + + // 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..ef92af54b --- /dev/null +++ b/src/opt/opt_params.pyg @@ -0,0 +1,28 @@ +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') + + )) + + + + + 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..ce78e68f3 --- /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..007ade793 --- /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..dcbbc3fae --- /dev/null +++ b/src/opt/opt_solver.cpp @@ -0,0 +1,406 @@ +/*++ +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); + 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..a18ad9540 --- /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..ff970556a --- /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..d3993dc49 --- /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..9d44c88cf --- /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/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/qe.cpp b/src/qe/qe.cpp index d229d8735..7372fdc2c 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -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_arith.h b/src/qe/qe_arith.h index c8f7e8b8d..725245fad 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #ifndef __QE_ARITH_H_ #define __QE_ARITH_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_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..41214acb3 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -238,67 +238,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) { diff --git a/src/qe/qe_sat_tactic.cpp b/src/qe/qe_sat_tactic.cpp index 2be32c02d..d2360762a 100644 --- a/src/qe/qe_sat_tactic.cpp +++ b/src/qe/qe_sat_tactic.cpp @@ -399,7 +399,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);); diff --git a/src/qe/qe_util.cpp b/src/qe/qe_util.cpp index 77396ac49..2cf723c08 100644 --- a/src/qe/qe_util.cpp +++ b/src/qe/qe_util.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "qe_util.h" #include "bool_rewriter.h" diff --git a/src/sat/sat_bceq.cpp b/src/sat/sat_bceq.cpp new file mode 100644 index 000000000..e3613515f --- /dev/null +++ b/src/sat/sat_bceq.cpp @@ -0,0 +1,494 @@ +/*++ +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 { + + 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(); + 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) { + clause_use_list::iterator it = uses.mk_iterator(); + while (!it.at_end()) { + clause& cls = it.curr(); + if (!cls.was_removed() && m_clauses[cls.id()]) { + clauses.push_back(&cls); + m_clauses[cls.id()] = 0; + } + it.next(); + } + } + + 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]); + } +#define MOVE_R_TO_L \ + + // 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. + 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; + } + + bool bceq::bce(clause& cls) { + svector live_clauses; + use_list ul; + m_use_list = &ul; + ul.init(m_solver.num_vars()); + for (unsigned i = 0; i < m_L.size(); ++i) { + ul.insert(*m_L[i]); + } + ul.insert(cls); + svector clauses(m_L), new_clauses; + literal_vector blits(m_L_blits), new_blits; + clauses.push_back(&cls); + blits.push_back(null_literal); + bool removed = false; + m_removed.reset(); + do { + removed = false; + for (unsigned i = 0; i < clauses.size(); ++i) { + clause& cls = *clauses[i]; + literal lit = find_blocked(cls); + if (lit != null_literal) { + m_removed.setx(cls.id(), true, false); + new_clauses.push_back(&cls); + new_blits.push_back(lit); + removed = true; + clauses[i] = clauses.back(); + blits[i] = blits.back(); + clauses.pop_back(); + blits.pop_back(); + --i; + } + } + } + while (removed); + if (clauses.empty()) { + m_L.reset(); + m_L_blits.reset(); + new_clauses.reverse(); + new_blits.reverse(); + m_L.append(new_clauses); + m_L_blits.append(new_blits); + } + if (!clauses.empty()) std::cout << "Number left after BCE: " << clauses.size() << "\n"; + return 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); + clause_use_list::iterator it = uses.mk_iterator(); + while (!it.at_end()) { + clause const& cls = it.curr(); + 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; + } + it.next(); + } + 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); + for (unsigned i = 0; i < vars.size(); ++i) { + std::cout << "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, 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(); + std::cout << "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..0b05c45cd --- /dev/null +++ b/src/sat/sat_bceq.h @@ -0,0 +1,78 @@ +/*++ +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 use_list; + class clause_use_list; + + class bceq { + typedef std::pair bin_clause; + typedef svector bin_clauses; + solver & m_solver; + use_list* m_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; + + void init(); + void register_clause(clause* cls); + void unregister_clause(clause* cls); + void pure_decompose(); + void pure_decompose(literal lit); + void pure_decompose(clause_use_list& 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_config.cpp b/src/sat/sat_config.cpp index fb81809b1..0c100aff2 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -107,6 +107,11 @@ 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_soft_assumptions = p.soft_assumptions(); + 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..29ef25f03 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -69,6 +69,12 @@ 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_soft_assumptions; + bool m_bcd; + symbol m_always_true; symbol m_always_false; diff --git a/src/sat/sat_justification.h b/src/sat/sat_justification.h index dd803feeb..b162662e8 100644 --- a/src/sat/sat_justification.h +++ b/src/sat/sat_justification.h @@ -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_mus.cpp b/src/sat/sat_mus.cpp new file mode 100644 index 000000000..4a811741f --- /dev/null +++ b/src/sat/sat_mus.cpp @@ -0,0 +1,282 @@ +/*++ +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 = 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); + } + + 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_min_partial(s.m_config.m_minimize_core_partial, false); + flet _disable_opt(s.m_config.m_optimize_model, false); + flet _is_active(m_is_active, true); + IF_VERBOSE(2, verbose_stream() << "(sat.mus " << s.get_core() << ")\n";); + reset(); + return mus1(); + } + + 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(2, verbose_stream() << "(opt.mus reducing core: " << core.size() << " new core: " << 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; + } + + 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(2, verbose_stream() << "miss core " << lit << "\n";); + ++core_miss; + } + else { + core_miss = 0; + TRACE("sat", tout << "new core: " << new_core << "\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; + } + } + TRACE("sat", tout << "new core: " << mus << "\n";); + set_core(); + IF_VERBOSE(2, 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(2, 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_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(2, verbose_stream() << "in core " << tabu[i] << "\n";); + reuse_model = true; + } + else { + IF_VERBOSE(2, 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..12cff4cec --- /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..ed4f4910a 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -18,4 +18,10 @@ 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'), + ('soft_assumptions', BOOL, False, 'disable assumptions that are forced during unit propagation'), + ('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_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..521990ea0 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -82,6 +82,7 @@ namespace sat { bool m_subsumption; unsigned m_subsumption_limit; + bool m_elim_vars; // stats unsigned m_num_blocked_clauses; 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..ad26554af --- /dev/null +++ b/src/sat/sat_sls.cpp @@ -0,0 +1,684 @@ +/*++ +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); + 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..530d4be0c --- /dev/null +++ b/src/sat/sat_sls.h @@ -0,0 +1,118 @@ +/*++ +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); + model const& get_model() { return m_best_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..40d02ebbb 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 @@ -39,6 +40,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 +50,10 @@ 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; } solver::~solver() { @@ -61,6 +68,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) { @@ -143,7 +151,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 +173,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: @@ -510,6 +534,10 @@ namespace sat { return found_undef ? l_undef : l_false; } + void solver::initialize_soft(unsigned sz, literal const* lits, double const* weights) { + m_wsls.set_soft(sz, lits, weights); + } + // ----------------------- // // Propagation @@ -669,6 +697,7 @@ namespace sat { } wlist.set_end(it2); } + SASSERT(m_qhead == m_trail.size()); SASSERT(!m_inconsistent); return true; } @@ -685,7 +714,9 @@ namespace sat { // Search // // ----------------------- - lbool solver::check() { + lbool solver::check(unsigned num_lits, literal const* lits) { + 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 +729,24 @@ namespace sat { init_search(); propagate(false); if (inconsistent()) return l_false; + init_assumptions(num_lits, lits); + 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 +766,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 +832,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 +851,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 +881,141 @@ 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) { + if (num_lits == 0 && m_user_scope_literals.empty()) { + return; + } + m_assumptions.reset(); + m_assumption_set.reset(); + 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()); + } + + for (unsigned i = 0; !inconsistent() && i < num_lits; ++i) { + literal lit = lits[i]; + SASSERT(is_external((lit).var())); + m_assumption_set.insert(lit); + + if (m_config.m_soft_assumptions) { + switch(value(lit)) { + case l_undef: + m_assumptions.push_back(lit); + assign(lit, justification()); + break; + case l_false: { + set_conflict(lit); + flet _min1(m_config.m_minimize_core, false); + flet _min2(m_config.m_minimize_core_partial, false); + resolve_conflict_for_unsat_core(); + SASSERT(m_core.size() <= m_assumptions.size()); + if (m_core.size() <= 3 || + m_core.size() <= i - m_assumptions.size() + 1) { + return; + } + else { + m_inconsistent = false; + } + break; + } + case l_true: + break; + } + propagate(false); + } + else { + m_assumptions.push_back(lit); + assign(lit, justification()); + // propagate(false); + } + } + } + + 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(); + } + + 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(); 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 +1027,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 +1049,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() { @@ -916,6 +1075,7 @@ namespace sat { 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 +1083,19 @@ 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_model.reset(); + m_model.append(m_wsls.get_model()); + } 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 +1114,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 +1137,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 +1169,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 +1444,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 +1502,6 @@ namespace sat { } bool solver::resolve_conflict_core() { - TRACE("sat_conflict", tout << "conflict detected\n";); m_stats.m_conflict++; m_conflicts++; @@ -1326,8 +1509,18 @@ 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_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); @@ -1431,7 +1624,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 +1635,154 @@ 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; + } + } + + 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 || m_config.m_minimize_core_partial) { + // TBD: + // apply optional clause minimization by detecting subsumed literals. + // initial experiment suggests it has no effect. + m_mus(); // ignore return value on cancelation. + m_model.reset(); + m_model.append(m_mus.get_model()); + m_model_is_current = !m_model.empty(); + } + } + + unsigned solver::get_max_lvl(literal consequent, justification js) { if (!m_ext) return scope_lvl(); @@ -1530,6 +1871,7 @@ namespace sat { } } + /** \brief js is an external justification. Collect its antecedents and store at m_ext_antecedents. */ @@ -1717,7 +2059,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 +2069,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 +2231,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 +2243,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 +2312,74 @@ 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; + if (m_user_scope_literal_pool.empty()) { + bool_var new_v = mk_var(true, false); + lit = literal(new_v, false); + } + else { + lit = m_user_scope_literal_pool.back(); + m_user_scope_literal_pool.pop_back(); + } + TRACE("sat", tout << "user_push: " << lit << "\n";); + m_user_scope_literals.push_back(lit); + } + + 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); + } + } + } + + 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_literal_pool.push_back(lit); + 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; + } + } + + void solver::pop_to_base_level() { + m_assumptions.reset(); + m_assumption_set.reset(); + pop(scope_lvl()); + } + // ----------------------- // // Misc @@ -1988,9 +2406,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); @@ -2103,6 +2522,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; @@ -2374,6 +2800,7 @@ namespace sat { m_del_clause = 0; m_minimized_lits = 0; m_dyn_sub_res = 0; + m_non_learned_generation = 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..e3628823f 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -32,6 +32,8 @@ 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" @@ -57,6 +59,7 @@ namespace sat { unsigned m_del_clause; unsigned m_minimized_lits; unsigned m_dyn_sub_res; + unsigned m_non_learned_generation; stats() { reset(); } void reset(); void collect_statistics(statistics & st) const; @@ -73,12 +76,15 @@ namespace sat { 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 +124,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,6 +138,10 @@ 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); @@ -143,7 +156,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; @@ -166,7 +179,7 @@ namespace sat { 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_bin_clause(literal l1, literal l2, bool learned); bool propagate_bin_clause(literal l1, literal l2); @@ -197,6 +210,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 +229,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); + ++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; + void initialize_soft(unsigned sz, literal const* lits, double const* weights); 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 +250,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,8 +271,10 @@ namespace sat { // // ----------------------- public: - lbool check(); + lbool check(unsigned num_lits = 0, literal const* lits = 0); 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; } protected: @@ -261,12 +284,17 @@ 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(); + void init_assumptions(unsigned num_lits, literal const* lits); + 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; @@ -312,8 +340,11 @@ namespace sat { literal_vector m_ext_antecedents; bool resolve_conflict(); bool resolve_conflict_core(); + void resolve_conflict_for_unsat_core(); unsigned get_max_lvl(literal consequent, justification js); void process_antecedent(literal antecedent, unsigned & num_marks); + void process_antecedent_for_unsat_core(literal antecedent); + void process_consequent_for_unsat_core(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 +370,23 @@ 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_user_scope_literal_pool; + 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); + public: + void user_push(); + void user_pop(unsigned num_scopes); + void pop_to_base_level(); // ----------------------- // // Simplification @@ -390,7 +430,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 +442,12 @@ namespace sat { void display(std::ostream & out) const; void display_watches(std::ostream & out) const; void display_dimacs(std::ostream & out) 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; 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_types.h b/src/sat/sat_types.h index db8de94a9..075c88fa1 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.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,44 @@ 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()); } 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/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/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 16184e2bc..ff201042b 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) { @@ -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); } diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index 7e38a1ca5..b4e9615a0 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.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,7 +57,7 @@ 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..e4d5e6df5 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -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/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/dimacs_frontend.cpp b/src/shell/dimacs_frontend.cpp index 0acd222dd..abaa2eace 100644 --- a/src/shell/dimacs_frontend.cpp +++ b/src/shell/dimacs_frontend.cpp @@ -22,6 +22,7 @@ Revision History: #include"timeout.h" #include"dimacs.h" #include"sat_solver.h" +#include"gparams.h" extern bool g_display_statistics; static sat::solver * g_solver = 0; @@ -63,11 +64,73 @@ 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); g_solver = &solver; @@ -85,17 +148,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, 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..5448d1af0 100644 --- a/src/shell/dimacs_frontend.h +++ b/src/shell/dimacs_frontend.h @@ -21,5 +21,5 @@ Revision History: 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..78d083e6e --- /dev/null +++ b/src/shell/opt_frontend.cpp @@ -0,0 +1,361 @@ + +/*++ +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_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; + } +}; + +class wcnf { + opt::context& opt; + ast_manager& m; + stream_buffer& in; + + app_ref read_clause(int& weight) { + int parsed_lit; + int var; + parsed_lit = in.parse_int(); + weight = parsed_lit; + 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(int& num_vars, int& num_clauses, int& max_weight) { + in.parse_token("wcnf"); + num_vars = in.parse_int(); + num_clauses = in.parse_int(); + max_weight = in.parse_int(); + } + +public: + + wcnf(opt::context& opt, stream_buffer& in): opt(opt), m(opt.get_manager()), in(in) { + opt.set_clausal(true); + } + + void parse() { + int 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 { + int 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_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"; + 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 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..df6070378 --- /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/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/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..0685bb90c 100644 --- a/src/smt/diff_logic.h +++ b/src/smt/diff_logic.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; } 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/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/params/smt_params.cpp b/src/smt/params/smt_params.cpp index a0f56663c..14f67bb5e 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -50,6 +50,7 @@ void smt_params::updt_params(params_ref const & p) { qi_params::updt_params(p); theory_arith_params::updt_params(p); theory_bv_params::updt_params(p); + theory_pb_params::updt_params(p); // theory_array_params::updt_params(p); updt_local_params(p); } diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index 2fbc9b6d4..579d509c7 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -25,6 +25,7 @@ Revision History: #include"theory_arith_params.h" #include"theory_array_params.h" #include"theory_bv_params.h" +#include"theory_pb_params.h" #include"theory_datatype_params.h" #include"preprocessor_params.h" #include"context_params.h" @@ -73,7 +74,8 @@ struct smt_params : public preprocessor_params, public qi_params, public theory_arith_params, public theory_array_params, - public theory_bv_params, + public theory_bv_params, + public theory_pb_params, public theory_datatype_params { bool m_display_proof; bool m_display_dot_proof; diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 97a326e4d..8be6110d5 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -44,12 +44,18 @@ def_module_params(module_name='smt', ('arith.branch_cut_ratio', UINT, 2, 'branch/cut ratio for linear integer arithmetic'), ('arith.int_eq_branch', BOOL, False, 'branching using derived integer equations'), ('arith.ignore_int', BOOL, False, 'treat integer variables as real'), + ('arith.dump_lemmas', BOOL, False, 'dump arithmetic theory lemmas to files'), + ('arith.greatest_error_pivot', BOOL, False, 'Pivoting strategy'), + ('pb.conflict_frequency', UINT, 1000, 'conflict frequency for Pseudo-Boolean theory'), + ('pb.learn_complements', BOOL, True, 'learn complement literals for Pseudo-Boolean theory'), + ('pb.enable_compilation', BOOL, True, 'enable compilation into sorting circuits for Pseudo-Boolean'), + ('pb.enable_simplex', BOOL, False, 'enable simplex to check rational feasibility'), ('array.weak', BOOL, False, 'weak array theory'), ('array.extensional', BOOL, True, 'extensional array theory'), - ('dack', UINT, 1, '0 - disable dynamic ackermannization, 1 - expand Leibniz\'s axiom if a congruence is the root of a conflict, 2 - expand Leibniz\'s axiom if a congruence is used during conflict resolution'), - ('dack.eq', BOOL, False, 'enable dynamic ackermannization for transtivity of equalities'), - ('dack.factor', DOUBLE, 0.1, 'number of instance per conflict'), - ('dack.gc', UINT, 2000, 'Dynamic ackermannization garbage collection frequency (per conflict)'), - ('dack.gc_inv_decay', DOUBLE, 0.8, 'Dynamic ackermannization garbage collection decay'), - ('dack.threshold', UINT, 10, ' number of times the congruence rule must be used before Leibniz\'s axiom is expanded') + ('dack', UINT, 1, '0 - disable dynamic ackermannization, 1 - expand Leibniz\'s axiom if a congruence is the root of a conflict, 2 - expand Leibniz\'s axiom if a congruence is used during conflict resolution'), + ('dack.eq', BOOL, False, 'enable dynamic ackermannization for transtivity of equalities'), + ('dack.factor', DOUBLE, 0.1, 'number of instance per conflict'), + ('dack.gc', UINT, 2000, 'Dynamic ackermannization garbage collection frequency (per conflict)'), + ('dack.gc_inv_decay', DOUBLE, 0.8, 'Dynamic ackermannization garbage collection decay'), + ('dack.threshold', UINT, 10, ' number of times the congruence rule must be used before Leibniz\'s axiom is expanded') )) diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 7287cac2f..fef7ca2a0 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -34,6 +34,7 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_arith_int_eq_branching = p.arith_int_eq_branch(); m_arith_ignore_int = p.arith_ignore_int(); m_arith_bound_prop = static_cast(p.arith_propagation_mode()); + m_arith_dump_lemmas = p.arith_dump_lemmas(); } diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 911c75d36..4394f46d3 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -27,7 +27,8 @@ enum arith_solver_id { AS_DIFF_LOGIC, AS_ARITH, AS_DENSE_DIFF_LOGIC, - AS_UTVPI + AS_UTVPI, + AS_OPTINF }; enum bound_prop_mode { diff --git a/src/smt/params/theory_pb_params.cpp b/src/smt/params/theory_pb_params.cpp new file mode 100644 index 000000000..6d980fe5d --- /dev/null +++ b/src/smt/params/theory_pb_params.cpp @@ -0,0 +1,28 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + theory_pb_params.cpp + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2014-01-01 + +Revision History: + +--*/ +#include"theory_pb_params.h" +#include"smt_params_helper.hpp" + +void theory_pb_params::updt_params(params_ref const & _p) { + smt_params_helper p(_p); + m_pb_conflict_frequency = p.pb_conflict_frequency(); + m_pb_learn_complements = p.pb_learn_complements(); + m_pb_enable_compilation = p.pb_enable_compilation(); + m_pb_enable_simplex = p.pb_enable_simplex(); +} diff --git a/src/smt/params/theory_pb_params.h b/src/smt/params/theory_pb_params.h new file mode 100644 index 000000000..0432ac633 --- /dev/null +++ b/src/smt/params/theory_pb_params.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_pb_params.h + +Abstract: + + + +Author: + + Nikolaj Bjorner (nbjorner) 2014-01-01 + +Revision History: + +--*/ +#ifndef _THEORY_PB_PARAMS_H_ +#define _THEORY_PB_PARAMS_H_ + +#include"params.h" + + +struct theory_pb_params { + unsigned m_pb_conflict_frequency; + bool m_pb_learn_complements; + bool m_pb_enable_compilation; + bool m_pb_enable_simplex; + theory_pb_params(params_ref const & p = params_ref()): + m_pb_conflict_frequency(1000), + m_pb_learn_complements(true), + m_pb_enable_compilation(true), + m_pb_enable_simplex(false) + {} + + void updt_params(params_ref const & p); +}; + +#endif /* _THEORY_PB_PARAMS_H_ */ + diff --git a/src/smt/smt_b_justification.h b/src/smt/smt_b_justification.h index 4798af4b0..5a82e36e8 100644 --- a/src/smt/smt_b_justification.h +++ b/src/smt/smt_b_justification.h @@ -54,7 +54,7 @@ namespace smt { m_data(BOXTAGINT(void*, l.index(), BIN_CLAUSE)) { } - explicit b_justification(justification * js): + explicit b_justification(justification * js): m_data(TAG(void*, js, JUSTIFICATION)) { SASSERT(js); } diff --git a/src/smt/smt_clause.h b/src/smt/smt_clause.h index 200508c53..c7378dc39 100644 --- a/src/smt/smt_clause.h +++ b/src/smt/smt_clause.h @@ -65,6 +65,13 @@ namespace smt { unsigned r = sizeof(clause) + sizeof(literal) * num_lits; if (k != CLS_AUX) r += sizeof(unsigned); + /* dvitek: Fix alignment issues on 64-bit platforms. The + * 'if' statement below probably isn't worthwhile since + * I'm guessing the allocator is probably going to round + * up internally anyway. + */ + //if (has_atoms || has_del_eh || has_justification) + r = (r + (sizeof(void*)-1)) & ~(sizeof(void*)-1); if (has_atoms) r += sizeof(expr*) * num_lits; if (has_del_eh) @@ -86,7 +93,13 @@ namespace smt { unsigned const * addr = get_activity_addr(); if (is_lemma()) addr ++; - return reinterpret_cast(addr); + /* dvitek: It would be better to use uintptr_t than + * size_t, but we need to wait until c++11 support is + * really available. + */ + size_t as_int = reinterpret_cast(addr); + as_int = (as_int + sizeof(void*)-1) & ~static_cast(sizeof(void*)-1); + return reinterpret_cast(as_int); } justification * const * get_justification_addr() const { diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index ec62efa1c..a68d4d820 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -370,7 +370,12 @@ namespace smt { tout << "conflict_lvl: " << m_conflict_lvl << " scope_lvl: " << m_ctx.get_scope_level() << " base_lvl: " << m_ctx.get_base_level() << " search_lvl: " << m_ctx.get_search_level() << "\n"; tout << "js.kind: " << js.get_kind() << "\n"; - m_ctx.display_literal(tout, consequent); tout << "\n";); + tout << "consequent: " << consequent << "\n"; + for (unsigned i = 0; i < m_assigned_literals.size(); ++i) { + tout << m_assigned_literals[i] << " "; + } + tout << "\n"; + ); // m_conflict_lvl can be smaller than m_ctx.get_search_level() when: // there are user level scopes created using the Z3 API, and @@ -1310,8 +1315,11 @@ namespace smt { } void conflict_resolution::process_antecedent_for_unsat_core(literal antecedent) { - TRACE("conflict", tout << "processing antecedent: "; m_ctx.display_literal(tout, antecedent); tout << "\n";); bool_var var = antecedent.var(); + TRACE("conflict", tout << "processing antecedent: "; + m_ctx.display_literal_info(tout << antecedent << " ", antecedent); + tout << (m_ctx.is_marked(var)?"marked":"not marked"); + tout << "\n";); if (!m_ctx.is_marked(var)) { m_ctx.set_mark(var); @@ -1341,13 +1349,14 @@ namespace smt { b_justification js = conflict; literal consequent = false_literal; - if (not_l != null_literal) + if (not_l != null_literal) { consequent = ~not_l; + } int idx = skip_literals_above_conflict_level(); if (not_l != null_literal) - process_antecedent_for_unsat_core(not_l); + process_antecedent_for_unsat_core(consequent); if (m_assigned_literals.empty()) { goto end_unsat_core; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index d7e18461b..36311af7e 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -199,6 +199,9 @@ namespace smt { bool context::bcp() { SASSERT(!inconsistent()); while (m_qhead < m_assigned_literals.size()) { + if (m_cancel_flag) { + return true; + } literal l = m_assigned_literals[m_qhead]; SASSERT(get_assignment(l) == l_true); m_qhead++; @@ -225,9 +228,6 @@ namespace smt { case l_true: break; } - if (m_cancel_flag) { - return true; - } } } @@ -1334,6 +1334,7 @@ namespace smt { TRACE("propagate_bool_var_enode_bug", tout << "var: " << v << " #" << bool_var2expr(v)->get_id() << "\n";); SASSERT(v < static_cast(m_b_internalized_stack.size())); enode * n = bool_var2enode(v); + CTRACE("mk_bool_var", !n, tout << "No enode for " << v << "\n";); bool sign = val == l_false; if (n->merge_tf()) add_eq(n, sign ? m_false_enode : m_true_enode, eq_justification(literal(v, sign))); @@ -2853,7 +2854,7 @@ namespace smt { void context::init_assumptions(unsigned num_assumptions, expr * const * assumptions) { reset_assumptions(); - m_bool_var2assumption.reset(); + m_literal2assumption.reset(); m_unsat_core.reset(); if (num_assumptions > 0) { // We must give a chance to the theories to propagate before we create a new scope... @@ -2869,16 +2870,16 @@ namespace smt { proof * pr = m_manager.mk_asserted(curr_assumption); internalize_assertion(curr_assumption, pr, 0); literal l = get_literal(curr_assumption); - m_bool_var2assumption.insert(l.var(), curr_assumption); + m_literal2assumption.insert(l.index(), curr_assumption); // mark_as_relevant(l); <<< not needed // internalize_assertion marked l as relevant. SASSERT(is_relevant(l)); - TRACE("assumptions", tout << mk_pp(curr_assumption, m_manager) << "\n";); + TRACE("assumptions", tout << l << ":" << mk_pp(curr_assumption, m_manager) << "\n";); if (m_manager.proofs_enabled()) assign(l, mk_justification(justification_proof_wrapper(*this, pr))); else assign(l, b_justification::mk_axiom()); - m_assumptions.push_back(l.var()); + m_assumptions.push_back(l); get_bdata(l.var()).m_assumption = true; } } @@ -2888,10 +2889,10 @@ namespace smt { } void context::reset_assumptions() { - bool_var_vector::iterator it = m_assumptions.begin(); - bool_var_vector::iterator end = m_assumptions.end(); + literal_vector::iterator it = m_assumptions.begin(); + literal_vector::iterator end = m_assumptions.end(); for (; it != end; ++it) - get_bdata(*it).m_assumption = false; + get_bdata(it->var()).m_assumption = false; m_assumptions.reset(); } @@ -2908,10 +2909,9 @@ namespace smt { literal l = *it; TRACE("unsat_core_bug", tout << "answer literal: " << l << "\n";); SASSERT(get_bdata(l.var()).m_assumption); - SASSERT(m_bool_var2assumption.contains(l.var())); - expr * a = 0; - m_bool_var2assumption.find(l.var(), a); - SASSERT(a); + if (!m_literal2assumption.contains(l.index())) l.neg(); + SASSERT(m_literal2assumption.contains(l.index())); + expr * a = m_literal2assumption[l.index()]; if (!already_found_assumptions.contains(a)) { already_found_assumptions.insert(a); m_unsat_core.push_back(a); @@ -2952,7 +2952,7 @@ namespace smt { \brief Execute some finalization code after performing the search. */ void context::check_finalize(lbool r) { - TRACE("after_search", display(tout);); + TRACE("after_search", display(tout << "result: " << r << "\n");); display_profile(verbose_stream()); } @@ -3236,10 +3236,17 @@ namespace smt { } ptr_vector::iterator it = m_theory_set.begin(); ptr_vector::iterator end = m_theory_set.end(); - for (; it != end; ++it) + for (; it != end && !inconsistent(); ++it) (*it)->restart_eh(); TRACE("mbqi_bug_detail", tout << "before instantiating quantifiers...\n";); - m_qmanager->restart_eh(); + if (!inconsistent()) { + m_qmanager->restart_eh(); + } + if (inconsistent()) { + VERIFY(!resolve_conflict()); + status = l_false; + break; + } } if (m_fparams.m_simplify_clauses) simplify_clauses(); @@ -3872,9 +3879,13 @@ namespace smt { bool context::is_shared(enode * n) const { n = n->get_root(); unsigned num_th_vars = n->get_num_th_vars(); + if (m_manager.is_ite(n->get_owner())) { + return true; + } switch (num_th_vars) { - case 0: + case 0: { return false; + } case 1: { if (m_qmanager->is_shared(n)) return true; @@ -3938,6 +3949,20 @@ namespace smt { return th->get_value(n, value); } + bool context::update_model(bool refinalize) { + final_check_status fcs = FC_DONE; + if (refinalize) { + fcs = final_check(); + } + TRACE("opt", tout << (refinalize?"refinalize":"no-op") << " " << fcs << "\n";); + if (fcs == FC_DONE) { + mk_proto_model(l_true); + m_model = m_proto_model->mk_model(); + } + + return fcs == FC_DONE; + } + void context::mk_proto_model(lbool r) { TRACE("get_model", display(tout); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 24a4c0f45..62bfacb38 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -206,9 +206,9 @@ namespace smt { // Unsat core extraction // // ----------------------------------- - typedef u_map bool_var2assumption; - bool_var_vector m_assumptions; - bool_var2assumption m_bool_var2assumption; // maps an expression associated with a literal to the original assumption + typedef u_map literal2assumption; + literal_vector m_assumptions; + literal2assumption m_literal2assumption; // maps an expression associated with a literal to the original assumption expr_ref_vector m_unsat_core; // ----------------------------------- @@ -337,6 +337,10 @@ namespace smt { return get_assignment(literal(v)); } + literal_vector const & assigned_literals() const { + return m_assigned_literals; + } + lbool get_assignment(expr * n) const; // Similar to get_assignment, but returns l_undef if n is not internalized. @@ -1391,6 +1395,8 @@ namespace smt { void get_model(model_ref & m) const; + bool update_model(bool refinalize); + void get_proto_model(proto_model_ref & m) const; unsigned get_num_asserted_formulas() const { return m_asserted_formulas.get_num_formulas(); } diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 9ce684440..3dca6a70b 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -103,9 +103,9 @@ namespace smt { void context::display_literal_info(std::ostream & out, literal l) const { l.display_compact(out, m_bool_var2expr.c_ptr()); if (l.sign()) - out << " (not " << mk_bounded_pp(bool_var2expr(l.var()), m_manager, 10) << ")\n"; + out << " (not " << mk_bounded_pp(bool_var2expr(l.var()), m_manager, 10) << ") "; else - out << " " << mk_bounded_pp(bool_var2expr(l.var()), m_manager, 10) << "\n"; + out << " " << mk_bounded_pp(bool_var2expr(l.var()), m_manager, 10) << " "; out << "relevant: " << is_relevant(bool_var2expr(l.var())) << ", val: " << get_assignment(l) << "\n"; } @@ -427,7 +427,7 @@ namespace smt { } expr_ref n(m_manager); literal2expr(~consequent, n); - pp.display(out, n); + pp.display_smt2(out, n); } static unsigned g_lemma_id = 0; @@ -437,9 +437,9 @@ namespace smt { void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent, const char * logic) const { char buffer[BUFFER_SZ]; #ifdef _WINDOWS - sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt", g_lemma_id); + sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt2", g_lemma_id); #else - sprintf(buffer, "lemma_%d.smt", g_lemma_id); + sprintf(buffer, "lemma_%d.smt2", g_lemma_id); #endif std::ofstream out(buffer); display_lemma_as_smt_problem(out, num_antecedents, antecedents, consequent, logic); @@ -468,7 +468,7 @@ namespace smt { } expr_ref n(m_manager); literal2expr(~consequent, n); - pp.display(out, n); + pp.display_smt2(out, n); } void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, @@ -476,9 +476,9 @@ namespace smt { literal consequent, const char * logic) const { char buffer[BUFFER_SZ]; #ifdef _WINDOWS - sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt", g_lemma_id); + sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt2", g_lemma_id); #else - sprintf(buffer, "lemma_%d.smt", g_lemma_id); + sprintf(buffer, "lemma_%d.smt2", g_lemma_id); #endif std::ofstream out(buffer); display_lemma_as_smt_problem(out, num_antecedents, antecedents, num_eq_antecedents, eq_antecedents, consequent, logic); diff --git a/src/smt/smt_farkas_util.cpp b/src/smt/smt_farkas_util.cpp new file mode 100644 index 000000000..c1a303299 --- /dev/null +++ b/src/smt/smt_farkas_util.cpp @@ -0,0 +1,356 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + smt_farkas_util.cpp + +Abstract: + + Utility for combining inequalities using coefficients obtained from Farkas lemmas. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-2. + +Revision History: + + NB. This utility is specialized to proofs generated by the arithmetic solvers. + +--*/ + +#include "smt_farkas_util.h" +#include "ast_pp.h" +#include "th_rewriter.h" +#include "bool_rewriter.h" + + +namespace smt { + + farkas_util::farkas_util(ast_manager& m): + m(m), + a(m), + m_ineqs(m), + m_split_literals(false), + m_time(0) { + } + + void farkas_util::mk_coerce(expr*& e1, expr*& e2) { + if (a.is_int(e1) && a.is_real(e2)) { + e1 = a.mk_to_real(e1); + } + else if (a.is_int(e2) && a.is_real(e1)) { + e2 = a.mk_to_real(e2); + } + } + + // TBD: arith_decl_util now supports coercion, so this should be deprecated. + app* farkas_util::mk_add(expr* e1, expr* e2) { + mk_coerce(e1, e2); + return a.mk_add(e1, e2); + } + + app* farkas_util::mk_mul(expr* e1, expr* e2) { + mk_coerce(e1, e2); + return a.mk_mul(e1, e2); + } + + app* farkas_util::mk_le(expr* e1, expr* e2) { + mk_coerce(e1, e2); + return a.mk_le(e1, e2); + } + + app* farkas_util::mk_ge(expr* e1, expr* e2) { + mk_coerce(e1, e2); + return a.mk_gt(e1, e2); + } + + app* farkas_util::mk_gt(expr* e1, expr* e2) { + mk_coerce(e1, e2); + return a.mk_gt(e1, e2); + } + + app* farkas_util::mk_lt(expr* e1, expr* e2) { + mk_coerce(e1, e2); + return a.mk_lt(e1, e2); + } + + void farkas_util::mul(rational const& c, expr* e, expr_ref& res) { + expr_ref tmp(m); + if (c.is_one()) { + tmp = e; + } + else { + tmp = mk_mul(a.mk_numeral(c, c.is_int() && a.is_int(e)), e); + } + res = mk_add(res, tmp); + } + + bool farkas_util::is_int_sort(app* c) { + SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); + SASSERT(a.is_int(c->get_arg(0)) || a.is_real(c->get_arg(0))); + return a.is_int(c->get_arg(0)); + } + + bool farkas_util::is_int_sort() { + SASSERT(!m_ineqs.empty()); + return is_int_sort(m_ineqs[0].get()); + } + + void farkas_util::normalize_coeffs() { + rational l(1); + for (unsigned i = 0; i < m_coeffs.size(); ++i) { + l = lcm(l, denominator(m_coeffs[i])); + } + if (!l.is_one()) { + for (unsigned i = 0; i < m_coeffs.size(); ++i) { + m_coeffs[i] *= l; + } + } + m_normalize_factor = l; + } + + app* farkas_util::mk_one() { + return a.mk_numeral(rational(1), true); + } + + app* farkas_util::fix_sign(bool is_pos, app* c) { + expr* x, *y; + SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); + bool is_int = is_int_sort(c); + if (is_int && is_pos && (a.is_lt(c, x, y) || a.is_gt(c, y, x))) { + return mk_le(mk_add(x, mk_one()), y); + } + if (is_int && !is_pos && (a.is_le(c, x, y) || a.is_ge(c, y, x))) { + // !(x <= y) <=> x > y <=> x >= y + 1 + return mk_ge(x, mk_add(y, mk_one())); + } + if (is_pos) { + return c; + } + if (a.is_le(c, x, y)) return mk_gt(x, y); + if (a.is_lt(c, x, y)) return mk_ge(x, y); + if (a.is_ge(c, x, y)) return mk_lt(x, y); + if (a.is_gt(c, x, y)) return mk_le(x, y); + UNREACHABLE(); + return c; + } + + void farkas_util::partition_ineqs() { + m_reps.reset(); + m_his.reset(); + ++m_time; + for (unsigned i = 0; i < m_ineqs.size(); ++i) { + m_reps.push_back(process_term(m_ineqs[i].get())); + } + unsigned head = 0; + while (head < m_ineqs.size()) { + unsigned r = find(m_reps[head]); + unsigned tail = head; + for (unsigned i = head+1; i < m_ineqs.size(); ++i) { + if (find(m_reps[i]) == r) { + ++tail; + if (tail != i) { + SASSERT(tail < i); + std::swap(m_reps[tail], m_reps[i]); + app_ref tmp(m); + tmp = m_ineqs[i].get(); + m_ineqs[i] = m_ineqs[tail].get(); + m_ineqs[tail] = tmp; + std::swap(m_coeffs[tail], m_coeffs[i]); + } + } + } + head = tail + 1; + m_his.push_back(head); + } + } + + unsigned farkas_util::find(unsigned idx) { + if (m_ts.size() <= idx) { + m_roots.resize(idx+1); + m_size.resize(idx+1); + m_ts.resize(idx+1); + m_roots[idx] = idx; + m_ts[idx] = m_time; + m_size[idx] = 1; + return idx; + } + if (m_ts[idx] != m_time) { + m_size[idx] = 1; + m_ts[idx] = m_time; + m_roots[idx] = idx; + return idx; + } + while (true) { + if (m_roots[idx] == idx) { + return idx; + } + idx = m_roots[idx]; + } + } + + void farkas_util::merge(unsigned i, unsigned j) { + i = find(i); + j = find(j); + if (i == j) { + return; + } + if (m_size[i] > m_size[j]) { + std::swap(i, j); + } + m_roots[i] = j; + m_size[j] += m_size[i]; + } + unsigned farkas_util::process_term(expr* e) { + unsigned r = e->get_id(); + ptr_vector todo; + ast_mark mark; + todo.push_back(e); + while (!todo.empty()) { + e = todo.back(); + todo.pop_back(); + if (mark.is_marked(e)) { + continue; + } + mark.mark(e, true); + if (is_uninterp(e)) { + merge(r, e->get_id()); + } + if (is_app(e)) { + app* a = to_app(e); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + todo.push_back(a->get_arg(i)); + } + } + } + return r; + } + expr_ref farkas_util::extract_consequence(unsigned lo, unsigned hi) { + bool is_int = is_int_sort(); + app_ref zero(a.mk_numeral(rational::zero(), is_int), m); + expr_ref res(m); + res = zero; + bool is_strict = false; + bool is_eq = true; + expr* x, *y; + for (unsigned i = lo; i < hi; ++i) { + app* c = m_ineqs[i].get(); + if (m.is_eq(c, x, y)) { + mul(m_coeffs[i], x, res); + mul(-m_coeffs[i], y, res); + } + if (a.is_lt(c, x, y) || a.is_gt(c, y, x)) { + mul(m_coeffs[i], x, res); + mul(-m_coeffs[i], y, res); + is_strict = true; + is_eq = false; + } + if (a.is_le(c, x, y) || a.is_ge(c, y, x)) { + mul(m_coeffs[i], x, res); + mul(-m_coeffs[i], y, res); + is_eq = false; + } + } + + zero = a.mk_numeral(rational::zero(), a.is_int(res)); + if (is_eq) { + res = m.mk_eq(res, zero); + } + else if (is_strict) { + res = mk_lt(res, zero); + } + else { + res = mk_le(res, zero); + } + res = m.mk_not(res); + th_rewriter rw(m); + params_ref params; + params.set_bool("gcd_rounding", true); + rw.updt_params(params); + proof_ref pr(m); + expr_ref result(m); + rw(res, result, pr); + fix_dl(result); + return result; + } + + void farkas_util::fix_dl(expr_ref& r) { + expr* e; + if (m.is_not(r, e)) { + r = e; + fix_dl(r); + r = m.mk_not(r); + return; + } + expr* e1, *e2, *e3, *e4; + if ((m.is_eq(r, e1, e2) || a.is_lt(r, e1, e2) || a.is_gt(r, e1, e2) || + a.is_le(r, e1, e2) || a.is_ge(r, e1, e2))) { + if (a.is_add(e1, e3, e4) && a.is_mul(e3)) { + r = m.mk_app(to_app(r)->get_decl(), a.mk_add(e4,e3), e2); + } + } + } + + void farkas_util::reset() { + m_ineqs.reset(); + m_coeffs.reset(); + } + + void farkas_util::add(rational const & coef, app * c) { + bool is_pos = true; + expr* e; + while (m.is_not(c, e)) { + is_pos = !is_pos; + c = to_app(e); + } + + if (!coef.is_zero() && !m.is_true(c)) { + m_coeffs.push_back(coef); + m_ineqs.push_back(fix_sign(is_pos, c)); + } + } + + expr_ref farkas_util::get() { + m_normalize_factor = rational::one(); + expr_ref res(m); + if (m_coeffs.empty()) { + res = m.mk_false(); + return res; + } + bool is_int = is_int_sort(); + if (is_int) { + normalize_coeffs(); + } + + if (m_split_literals) { + // partition equalities into variable disjoint sets. + // take the conjunction of these instead of the + // linear combination. + partition_ineqs(); + expr_ref_vector lits(m); + unsigned lo = 0; + for (unsigned i = 0; i < m_his.size(); ++i) { + unsigned hi = m_his[i]; + lits.push_back(extract_consequence(lo, hi)); + lo = hi; + } + bool_rewriter(m).mk_or(lits.size(), lits.c_ptr(), res); + IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } }); + } + else { + res = extract_consequence(0, m_coeffs.size()); + } + + TRACE("arith", + for (unsigned i = 0; i < m_coeffs.size(); ++i) { + tout << m_coeffs[i] << " * (" << mk_pp(m_ineqs[i].get(), m) << ") "; + } + tout << "\n"; + tout << res << "\n"; + ); + + return res; + } +} + diff --git a/src/smt/smt_farkas_util.h b/src/smt/smt_farkas_util.h new file mode 100644 index 000000000..0152ee289 --- /dev/null +++ b/src/smt/smt_farkas_util.h @@ -0,0 +1,96 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + smt_farkas_util.h + +Abstract: + + Utility for combining inequalities using coefficients obtained from Farkas lemmas. + +Author: + + Nikolaj Bjorner (nbjorne) 2013-11-2. + +Revision History: + + NB. This utility is specialized to proofs generated by the arithmetic solvers. + +--*/ + +#ifndef __FARKAS_UTIL_H_ +#define __FARKAS_UTIL_H_ + +#include "arith_decl_plugin.h" + +namespace smt { + + class farkas_util { + ast_manager& m; + arith_util a; + app_ref_vector m_ineqs; + vector m_coeffs; + rational m_normalize_factor; + // utilities for separating coefficients + bool m_split_literals; + unsigned m_time; + unsigned_vector m_roots, m_size, m_his, m_reps, m_ts; + + void mk_coerce(expr*& e1, expr*& e2); + app* mk_add(expr* e1, expr* e2); + app* mk_mul(expr* e1, expr* e2); + app* mk_le(expr* e1, expr* e2); + app* mk_ge(expr* e1, expr* e2); + app* mk_gt(expr* e1, expr* e2); + app* mk_lt(expr* e1, expr* e2); + void mul(rational const& c, expr* e, expr_ref& res); + bool is_int_sort(app* c); + bool is_int_sort(); + void normalize_coeffs(); + app* mk_one(); + app* fix_sign(bool is_pos, app* c); + void partition_ineqs(); + unsigned find(unsigned idx); + void merge(unsigned i, unsigned j); + unsigned process_term(expr* e); + expr_ref extract_consequence(unsigned lo, unsigned hi); + void fix_dl(expr_ref& r); + + public: + farkas_util(ast_manager& m); + + /** + \brief Reset state + */ + void reset(); + + /** + \brief add a multiple of constraint c to the current state + */ + void add(rational const & coef, app * c); + + /** + \brief Extract the complement of premises multiplied by Farkas coefficients. + */ + expr_ref get(); + + /** + \brief Coefficients are normalized for integer problems. + Retrieve multiplicant for normalization. + */ + rational const& get_normalize_factor() const { return m_normalize_factor; } + + /** + \brief extract one or multiple consequences based on literal partition. + Multiple consequences are strongst modulo a partition of variables. + Consequence generation under literal partitioning maintains difference logic constraints. + That is, if the original constraints are difference logic, then the consequent + produced by literal partitioning is also difference logic. + */ + void set_split_literals(bool f) { m_split_literals = f; } + + }; +} + +#endif diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 42e761b61..4f810727a 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -814,6 +814,7 @@ namespace smt { */ bool_var context::mk_bool_var(expr * n) { SASSERT(!b_internalized(n)); + //SASSERT(!m_manager.is_not(n)); unsigned id = n->get_id(); bool_var v = m_b_internalized_stack.size(); #ifndef _EXTERNAL_RELEASE @@ -828,7 +829,7 @@ namespace smt { } #endif TRACE("mk_bool_var", tout << "creating boolean variable: " << v << " for:\n" << mk_pp(n, m_manager) << "\n";); - TRACE("mk_var_bug", tout << "mk_bool: " << v << "\n";); + TRACE("mk_var_bug", tout << "mk_bool: " << v << "\n";); set_bool_var(id, v); m_bdata.reserve(v+1); m_activity.reserve(v+1); @@ -1410,6 +1411,10 @@ namespace smt { void context::mk_th_axiom(theory_id tid, unsigned num_lits, literal * lits, unsigned num_params, parameter * params) { justification * js = 0; + TRACE("mk_th_axiom", + display_literals_verbose(tout, num_lits, lits); + tout << "\n";); + if (m_manager.proofs_enabled()) { js = mk_justification(theory_axiom_justification(tid, m_region, num_lits, lits, num_params, params)); } @@ -1424,13 +1429,11 @@ namespace smt { void context::mk_th_axiom(theory_id tid, literal l1, literal l2, unsigned num_params, parameter * params) { literal ls[2] = { l1, l2 }; - TRACE("mk_th_axiom", display_literal(tout, l1); tout << " "; display_literal(tout, l2); tout << "\n";); mk_th_axiom(tid, 2, ls, num_params, params); } void context::mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { literal ls[3] = { l1, l2, l3 }; - TRACE("mk_th_axiom", display_literal(tout, l1); tout << " "; display_literal(tout, l2); tout << " "; display_literal(tout, l3); tout << "\n";); mk_th_axiom(tid, 3, ls, num_params, params); } diff --git a/src/smt/smt_justification.cpp b/src/smt/smt_justification.cpp index 81f496187..de3594fcc 100644 --- a/src/smt/smt_justification.cpp +++ b/src/smt/smt_justification.cpp @@ -308,7 +308,8 @@ namespace smt { simple_justification(r, num_lits, lits), m_num_eqs(num_eqs) { m_eqs = new (r) enode_pair[num_eqs]; - memcpy(m_eqs, eqs, sizeof(enode_pair) * num_eqs); + if (num_eqs != 0) + memcpy(m_eqs, eqs, sizeof(enode_pair) * num_eqs); DEBUG_CODE({ for (unsigned i = 0; i < num_eqs; i++) { SASSERT(eqs[i].first->get_root() == eqs[i].second->get_root()); diff --git a/src/smt/smt_justification.h b/src/smt/smt_justification.h index 4f402daf2..ba306ba73 100644 --- a/src/smt/smt_justification.h +++ b/src/smt/smt_justification.h @@ -225,6 +225,7 @@ namespace smt { virtual proof * mk_proof(conflict_resolution & cr) = 0; virtual char const * get_name() const { return "simple"; } + }; class simple_theory_justification : public simple_justification { @@ -274,6 +275,7 @@ namespace smt { virtual char const * get_name() const { return "theory-propagation"; } + }; class theory_conflict_justification : public simple_theory_justification { diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index e6d21a1e2..98ad5e51a 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -30,6 +30,7 @@ Revision History: #include"theory_dummy.h" #include"theory_dl.h" #include"theory_seq_empty.h" +#include"theory_pb.h" #include"theory_fpa.h" namespace smt { @@ -115,7 +116,7 @@ namespace smt { setup_LRA(); else if (m_logic == "QF_FP") setup_QF_FP(); - else if (m_logic == "QF_FPBV") + else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") setup_QF_FPBV(); else setup_unknown(); @@ -275,7 +276,7 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) { - if (!st.m_has_rational && !m_params.m_model && st.m_arith_k_sum < rational(INT_MAX / 8)) + if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_smi, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); @@ -283,7 +284,7 @@ namespace smt { else { if (m_params.m_arith_auto_config_simplex || st.m_num_uninterpreted_constants > 4 * st.m_num_bool_constants || st.m_num_ite_terms > 0 /* theory_rdl and theory_frdl do not support ite-terms */) { - // if (!st.m_has_rational && !m_params.m_model && st.m_arith_k_sum < rational(INT_MAX / 8)) { + // if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) { // TRACE("rdl_bug", tout << "using theory_smi_arith\n";); // m_context.register_plugin(alloc(smt::theory_smi_arith, m_manager, m_params)); // } @@ -296,7 +297,7 @@ namespace smt { m_params.m_arith_bound_prop = BP_NONE; m_params.m_arith_propagation_strategy = ARITH_PROP_AGILITY; m_params.m_arith_add_binary_bounds = true; - if (!st.m_has_rational && !m_params.m_model && st.m_arith_k_sum < rational(INT_MAX / 8)) + if (!st.m_has_rational && !m_params.m_model && st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_frdl, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_rdl, m_manager, m_params)); @@ -305,7 +306,7 @@ namespace smt { } void setup::setup_QF_IDL() { - TRACE("setup", tout << "setup_QF_IDL(st)\n";); + TRACE("setup", tout << "setup_QF_IDL()\n";); m_params.m_relevancy_lvl = 0; m_params.m_arith_expand_eqs = true; m_params.m_arith_reflect = false; @@ -355,13 +356,14 @@ namespace smt { else if (!m_params.m_arith_auto_config_simplex && is_dense(st)) { TRACE("setup", tout << "using dense diff logic...\n";); m_params.m_phase_selection = PS_CACHING_CONSERVATIVE; - if (st.m_arith_k_sum < rational(INT_MAX / 8)) + if (st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params)); + } else { - // if (st.m_arith_k_sum < rational(INT_MAX / 8)) { + // if (st.arith_k_sum_is_small()) { // TRACE("setup", tout << "using small integer simplex...\n";); // m_context.register_plugin(alloc(smt::theory_si_arith, m_manager, m_params)); // } @@ -378,6 +380,7 @@ namespace smt { m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; m_params.m_arith_eq_bounds = true; + m_params.m_arith_expand_eqs = true; m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; @@ -403,7 +406,7 @@ namespace smt { if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } - else if (st.m_arith_k_sum < rational(INT_MAX / 8)) + else if (st.arith_k_sum_is_small()) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_dense_i, m_manager, m_params)); @@ -418,8 +421,8 @@ namespace smt { if (m_manager.proofs_enabled()) { m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); } - // else if (st.m_arith_k_sum < rational(INT_MAX / 8)) - // m_context.register_plugin(alloc(smt::theory_si_arith, m_manager, m_params)); + // else if (st.arith_k_sum_is_small()) + // m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); } @@ -599,7 +602,7 @@ namespace smt { m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; m_params.m_random_initial_activity = IA_ZERO; } - // if (st.m_num_arith_ineqs == st.m_num_diff_ineqs && st.m_num_arith_eqs == st.m_num_diff_eqs && st.m_arith_k_sum < rational(INT_MAX / 8)) + // if (st.m_num_arith_ineqs == st.m_num_diff_ineqs && st.m_num_arith_eqs == st.m_num_diff_eqs && st.arith_k_sum_is_small()) // m_context.register_plugin(new smt::theory_si_arith(m_manager, m_params)); // else setup_i_arith(); @@ -703,15 +706,27 @@ namespace smt { } void setup::setup_mi_arith() { - m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); + if (m_params.m_arith_mode == AS_OPTINF) { + m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); + } + else { + m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); + } } void setup::setup_arith() { + static_features st(m_manager); + IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); + st.collect(m_context.get_num_asserted_formulas(), m_context.get_asserted_formulas()); + IF_VERBOSE(1000, st.display_primitive(verbose_stream());); + m_params.m_arith_fixnum = st.arith_k_sum_is_small(); + m_params.m_arith_int_only = !st.m_has_rational && !st.m_has_real; switch(m_params.m_arith_mode) { case AS_NO_ARITH: m_context.register_plugin(alloc(smt::theory_dummy, m_manager.mk_family_id("arith"), "no arithmetic")); break; case AS_DIFF_LOGIC: + m_params.m_arith_expand_eqs = true; if (m_params.m_arith_fixnum) { if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_fidl, m_manager, m_params)); @@ -726,6 +741,7 @@ namespace smt { } break; case AS_DENSE_DIFF_LOGIC: + m_params.m_arith_expand_eqs = true; if (m_params.m_arith_fixnum) { if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_dense_si, m_manager, m_params)); @@ -740,11 +756,15 @@ namespace smt { } break; case AS_UTVPI: + m_params.m_arith_expand_eqs = true; if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager)); else m_context.register_plugin(alloc(smt::theory_rutvpi, m_manager)); break; + case AS_OPTINF: + m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); + break; default: if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); @@ -795,6 +815,10 @@ namespace smt { m_context.register_plugin(alloc(theory_seq_empty, m_manager)); } + void setup::setup_card() { + m_context.register_plugin(alloc(theory_pb, m_manager, m_params)); + } + void setup::setup_fpa() { setup_bv(); m_context.register_plugin(alloc(theory_fpa, m_manager)); @@ -807,6 +831,7 @@ namespace smt { setup_datatypes(); setup_dl(); setup_seq(); + setup_card(); setup_fpa(); } diff --git a/src/smt/smt_setup.h b/src/smt/smt_setup.h index 6cbcb9602..27c0f03f2 100644 --- a/src/smt/smt_setup.h +++ b/src/smt/smt_setup.h @@ -42,7 +42,7 @@ namespace smt { class setup { context & m_context; ast_manager & m_manager; - smt_params & m_params; + smt_params & m_params; symbol m_logic; bool m_already_configured; void setup_auto_config(); @@ -93,8 +93,8 @@ namespace smt { void setup_bv(); void setup_arith(); void setup_dl(); - void setup_seq(); - void setup_instgen(); + void setup_seq(); + void setup_card(); void setup_i_arith(); void setup_mi_arith(); void setup_fpa(); diff --git a/src/smt/spanning_tree.h b/src/smt/spanning_tree.h new file mode 100644 index 000000000..5bd5b9fb0 --- /dev/null +++ b/src/smt/spanning_tree.h @@ -0,0 +1,82 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + spanning_tree.h + +Abstract: + + Represent spanning trees with needed operations for Network Simplex + +Author: + + Anh-Dung Phan (t-anphan) 2013-11-06 + +Notes: + +--*/ +#ifndef _SPANNING_TREE_H_ +#define _SPANNING_TREE_H_ + +#include "diff_logic.h" +#include "spanning_tree_base.h" + +namespace smt { + + template + class thread_spanning_tree : public spanning_tree_base, protected Ext { + protected: + typedef dl_edge edge; + typedef dl_graph graph; + typedef typename Ext::numeral numeral; + typedef typename Ext::fin_numeral fin_numeral; + + // Store the parent of a node i in the spanning tree + svector m_pred; + // Store the number of edge on the path from node i to the root + svector m_depth; + svector m_thread; // Store the pointer from node i to the next node in depth-first search order + + svector m_tree; // i |-> edge between (i, m_pred[i]) + + node_id m_root_t2; + + graph & m_graph; + + void swap_order(node_id q, node_id v); + node_id find_rev_thread(node_id n) const; + void fix_depth(node_id start, node_id after_end); + node_id get_final(int start); + bool is_preorder_traversal(node_id start, node_id end); + node_id get_common_ancestor(node_id u, node_id v); + bool is_forward_edge(edge_id e_id) const; + bool is_ancestor_of(node_id ancestor, node_id child); + + public: + thread_spanning_tree(graph & g); + + virtual void initialize(svector const & tree); + void get_descendants(node_id start, svector & descendants); + + virtual void update(edge_id enter_id, edge_id leave_id); + void get_path(node_id start, node_id end, svector & path, svector & against); + bool in_subtree_t2(node_id child); + + bool check_well_formed(); + }; + + template + class basic_spanning_tree : public thread_spanning_tree { + private: + graph * m_tree_graph; + + public: + basic_spanning_tree(graph & g); + void initialize(svector const & tree); + void update(edge_id enter_id, edge_id leave_id); + }; + +} + +#endif diff --git a/src/smt/spanning_tree_base.h b/src/smt/spanning_tree_base.h new file mode 100644 index 000000000..3d3ee7b3a --- /dev/null +++ b/src/smt/spanning_tree_base.h @@ -0,0 +1,53 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + spanning_tree_base.h + +Abstract: + + Represent spanning trees with needed operations for Network Simplex + +Author: + + Anh-Dung Phan (t-anphan) 2013-11-06 + +Notes: + +--*/ + +#ifndef _SPANNING_TREE_BASE_H_ +#define _SPANNING_TREE_BASE_H_ + +#include "util.h" +#include "vector.h" + +namespace smt { + template + inline std::string pp_vector(std::string const & label, TV v) { + std::ostringstream oss; + oss << label << " "; + for (unsigned i = 0; i < v.size(); ++i) { + oss << v[i] << " "; + } + oss << std::endl; + return oss.str(); + } + + class spanning_tree_base { + public: + typedef int node_id; + typedef int edge_id; + virtual void initialize(svector const & tree) = 0; + virtual void get_descendants(node_id start, svector & descendants) = 0; + + virtual void update(edge_id enter_id, edge_id leave_id) = 0; + virtual void get_path(node_id start, node_id end, svector & path, svector & against) = 0; + virtual bool in_subtree_t2(node_id child) = 0; + + virtual bool check_well_formed() = 0; + }; +} + +#endif diff --git a/src/smt/spanning_tree_def.h b/src/smt/spanning_tree_def.h new file mode 100644 index 000000000..5dbcd2b8b --- /dev/null +++ b/src/smt/spanning_tree_def.h @@ -0,0 +1,489 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + spanning_tree_def.h + +Abstract: + + +Author: + + Anh-Dung Phan (t-anphan) 2013-11-06 + +Notes: + +--*/ + +#ifndef _SPANNING_TREE_DEF_H_ +#define _SPANNING_TREE_DEF_H_ + +#include "spanning_tree.h" + +namespace smt { + + template + thread_spanning_tree::thread_spanning_tree(graph & g) : + m_graph(g) { + } + + template + void thread_spanning_tree::initialize(svector const & tree) { + m_tree = tree; + + unsigned num_nodes = m_graph.get_num_nodes(); + m_pred.resize(num_nodes); + m_depth.resize(num_nodes); + m_thread.resize(num_nodes); + + node_id root = num_nodes - 1; + m_pred[root] = -1; + m_depth[root] = 0; + m_thread[root] = 0; + + // Create artificial edges from/to root node to/from other nodes and initialize the spanning tree + for (int i = 0; i < root; ++i) { + m_pred[i] = root; + m_depth[i] = 1; + m_thread[i] = i + 1; + } + + TRACE("network_flow", { + tout << pp_vector("Predecessors", m_pred) << pp_vector("Threads", m_thread); + tout << pp_vector("Depths", m_depth) << pp_vector("Tree", m_tree); + }); + } + + template + typename thread_spanning_tree::node_id thread_spanning_tree::get_common_ancestor(node_id u, node_id v) { + while (u != v) { + if (m_depth[u] > m_depth[v]) + u = m_pred[u]; + else + v = m_pred[v]; + } + return u; + } + + template + void thread_spanning_tree::get_path(node_id start, node_id end, svector & path, svector & against) { + node_id join = get_common_ancestor(start, end); + path.reset(); + while (start != join) { + edge_id e_id = m_tree[start]; + path.push_back(e_id); + against.push_back(is_forward_edge(e_id)); + start = m_pred[start]; + } + while (end != join) { + edge_id e_id = m_tree[end]; + path.push_back(e_id); + against.push_back(!is_forward_edge(e_id)); + end = m_pred[end]; + } + } + + template + bool thread_spanning_tree::is_forward_edge(edge_id e_id) const { + node_id start = m_graph.get_source(e_id); + node_id end = m_graph.get_target(e_id); + SASSERT(m_pred[start] == end || m_pred[end] == start); + return m_pred[start] == end; + } + + template + void thread_spanning_tree::get_descendants(node_id start, svector & descendants) { + descendants.reset(); + descendants.push_back(start); + node_id u = m_thread[start]; + while (m_depth[u] > m_depth[start]) { + descendants.push_back(u); + u = m_thread[u]; + } + + } + + template + bool thread_spanning_tree::in_subtree_t2(node_id child) { + if (m_depth[child] < m_depth[m_root_t2]) { + return false; + } + return is_ancestor_of(m_root_t2, child); + } + + template + bool thread_spanning_tree::is_ancestor_of(node_id ancestor, node_id child) { + for (node_id n = child; n != -1; n = m_pred[n]) { + if (n == ancestor) { + return true; + } + } + return false; + } + + /** + \brief add entering_edge, remove leaving_edge from spanning tree. + + Old tree: New tree: + root root + / \ / \ + x y x y + / \ / \ / \ / \ + u s u s + | / / + v w v w + / \ \ / \ \ + z p z p + \ \ / + q q + */ + template + void thread_spanning_tree::update(edge_id enter_id, edge_id leave_id) { + node_id p = m_graph.get_source(enter_id); + node_id q = m_graph.get_target(enter_id); + node_id u = m_graph.get_source(leave_id); + node_id v = m_graph.get_target(leave_id); + + if (m_pred[u] == v) { + std::swap(u, v); + } + + SASSERT(m_pred[v] == u); + + if (is_ancestor_of(v, p)) { + std::swap(p, q); + } + + SASSERT(is_ancestor_of(v, q)); + + TRACE("network_flow", { + tout << "update_spanning_tree: (" << p << ", " << q << ") enters, ("; + tout << u << ", " << v << ") leaves\n"; + }); + + // Old threads: alpha -> v -*-> f(v) -> beta | p -*-> f(p) -> gamma + // New threads: alpha -> beta | p -*-> f(p) -> v -*-> f(v) -> gamma + + node_id f_p = get_final(p); + node_id f_v = get_final(v); + node_id alpha = find_rev_thread(v); + node_id beta = m_thread[f_v]; + node_id gamma = m_thread[f_p]; + + if (v != gamma) { + m_thread[alpha] = beta; + m_thread[f_p] = v; + m_thread[f_v] = gamma; + } + + node_id old_pred = m_pred[q]; + // Update stem nodes from q to v + if (q != v) { + for (node_id n = q; n != v; ) { + SASSERT(old_pred != u); // the last processed node_id is v + SASSERT(-1 != m_pred[old_pred]); + int next_old_pred = m_pred[old_pred]; + swap_order(n, old_pred); + m_tree[old_pred] = m_tree[n]; + n = old_pred; + old_pred = next_old_pred; + } + } + + m_pred[q] = p; + m_tree[q] = enter_id; + m_root_t2 = q; + + node_id after_final_q = (v == gamma) ? beta : gamma; + fix_depth(q, after_final_q); + + SASSERT(!in_subtree_t2(p)); + SASSERT(in_subtree_t2(q)); + SASSERT(!in_subtree_t2(u)); + SASSERT(in_subtree_t2(v)); + + TRACE("network_flow", { + tout << pp_vector("Predecessors", m_pred) << pp_vector("Threads", m_thread); + tout << pp_vector("Depths", m_depth) << pp_vector("Tree", m_tree); + }); + } + + /** + swap v and q in tree. + - fixup m_thread + - fixup m_pred + + Case 1: final(q) == final(v) + ------- + Old thread: prev -> v -*-> alpha -> q -*-> final(q) -> next + New thread: prev -> q -*-> final(q) -> v -*-> alpha -> next + + Case 2: final(q) != final(v) + ------- + Old thread: prev -> v -*-> alpha -> q -*-> final(q) -> beta -*-> final(v) -> next + New thread: prev -> q -*-> final(q) -> v -*-> alpha -> beta -*-> final(v) -> next + + */ + template + void thread_spanning_tree::swap_order(node_id q, node_id v) { + SASSERT(q != v); + SASSERT(m_pred[q] == v); + SASSERT(is_preorder_traversal(v, get_final(v))); + node_id prev = find_rev_thread(v); + node_id f_q = get_final(q); + node_id f_v = get_final(v); + node_id next = m_thread[f_v]; + node_id alpha = find_rev_thread(q); + + if (f_q == f_v) { + SASSERT(f_q != v && alpha != next); + m_thread[f_q] = v; + m_thread[alpha] = next; + f_q = alpha; + } + else { + node_id beta = m_thread[f_q]; + SASSERT(f_q != v && alpha != beta); + m_thread[f_q] = v; + m_thread[alpha] = beta; + f_q = f_v; + } + SASSERT(prev != q); + m_thread[prev] = q; + m_pred[v] = q; + // Notes: f_q has to be used since m_depth hasn't been updated yet. + SASSERT(is_preorder_traversal(q, f_q)); + } + + /** + \brief Check invariants of main data-structures. + + Spanning tree of m_graph + root is represented using: + + svector m_states; edge_id |-> edge_state + svector m_pred; node_id |-> node + svector m_depth; node_id |-> int + svector m_thread; node_id |-> node + + Tree is determined by m_pred: + - m_pred[root] == -1 + - m_pred[n] = m != n for each node_id n, acyclic until reaching root. + - m_depth[m_pred[n]] + 1 == m_depth[n] for each n != root + + m_thread is a linked list traversing all nodes. + Furthermore, the nodes linked in m_thread follows a + depth-first traversal order. + + */ + template + bool thread_spanning_tree::check_well_formed() { + node_id root = m_pred.size()-1; + + // Check that m_thread traverses each node. + // This gets checked using union-find as well. + svector found(m_thread.size(), false); + found[root] = true; + for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { + SASSERT(x != m_thread[x]); + found[x] = true; + } + for (unsigned i = 0; i < found.size(); ++i) { + SASSERT(found[i]); + } + + // m_pred is acyclic, and points to root. + SASSERT(m_pred[root] == -1); + SASSERT(m_depth[root] == 0); + for (node_id i = 0; i < root; ++i) { + SASSERT(m_depth[m_pred[i]] < m_depth[i]); + } + + // m_depth[x] denotes distance from x to the root node + for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { + SASSERT(m_depth[x] > 0); + SASSERT(m_depth[x] == m_depth[m_pred[x]] + 1); + } + + // m_thread forms a spanning tree over [0..root] + // Union-find structure + svector roots(m_pred.size(), -1); + + for (node_id x = m_thread[root]; x != root; x = m_thread[x]) { + node_id y = m_pred[x]; + // We are now going to check the edge between x and y + SASSERT(find(roots, x) != find(roots, y)); + merge(roots, x, y); + } + + // All nodes belong to the same spanning tree + for (unsigned i = 0; i < roots.size(); ++i) { + SASSERT(roots[i] + roots.size() == 0 || roots[i] >= 0); + } + + for (unsigned i = 0; i < m_tree.size(); ++i) { + node_id src = m_graph.get_source(m_tree[i]); + node_id tgt = m_graph.get_target(m_tree[i]); + SASSERT(m_pred[src] == tgt || m_pred[tgt] == src); + } + + return true; + } + + static unsigned find(svector& roots, unsigned x) { + unsigned old_x = x; + while (roots[x] >= 0) { + x = roots[x]; + } + SASSERT(roots[x] < 0); + if (old_x != x) { + roots[old_x] = x; + } + return x; + } + + static void merge(svector& roots, unsigned x, unsigned y) { + x = find(roots, x); + y = find(roots, y); + SASSERT(roots[x] < 0 && roots[y] < 0); + if (x == y) { + return; + } + if (roots[x] > roots[y]) { + std::swap(x, y); + } + SASSERT(roots[x] <= roots[y]); + roots[x] += roots[y]; + roots[y] = x; + } + + /** + \brief find node_id that points to 'n' in m_thread + */ + template + typename thread_spanning_tree::node_id thread_spanning_tree::find_rev_thread(node_id n) const { + node_id ancestor = m_pred[n]; + SASSERT(ancestor != -1); + while (m_thread[ancestor] != n) { + ancestor = m_thread[ancestor]; + } + return ancestor; + } + + template + void thread_spanning_tree::fix_depth(node_id start, node_id after_end) { + while (start != after_end) { + SASSERT(m_pred[start] != -1); + m_depth[start] = m_depth[m_pred[start]]+1; + start = m_thread[start]; + } + } + + template + typename thread_spanning_tree::node_id thread_spanning_tree::get_final(int start) { + int n = start; + while (m_depth[m_thread[n]] > m_depth[start]) { + n = m_thread[n]; + } + return n; + } + + template + bool thread_spanning_tree::is_preorder_traversal(node_id start, node_id end) { + // get children of start + uint_set children; + children.insert(start); + node_id root = m_pred.size()-1; + for (int i = 0; i < root; ++i) { + for (int j = 0; j < root; ++j) { + if (children.contains(m_pred[j])) { + children.insert(j); + } + } + } + // visit children using m_thread + children.remove(start); + do { + start = m_thread[start]; + SASSERT(children.contains(start)); + children.remove(start); + } + while (start != end); + SASSERT(children.empty()); + return true; + } + + // Basic spanning tree + template + basic_spanning_tree::basic_spanning_tree(graph & g) : thread_spanning_tree(g) { + } + + template + void basic_spanning_tree::initialize(svector const & tree) { + m_tree_graph = alloc(graph); + m_tree = tree; + unsigned num_nodes = m_graph.get_num_nodes(); + for (unsigned i = 0; i < num_nodes; ++i) { + m_tree_graph->init_var(i); + } + + vector const & es = m_graph.get_all_edges(); + svector::const_iterator it = m_tree.begin(), end = m_tree.end(); + for(; it != end; ++it) { + edge const & e = es[*it]; + m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); + } + + node_id root = num_nodes - 1; + m_tree_graph->bfs_undirected(root, m_pred, m_depth); + m_tree_graph->dfs_undirected(root, m_thread); + } + + template + void basic_spanning_tree::update(edge_id enter_id, edge_id leave_id) { + if (m_tree_graph) dealloc(m_tree_graph); + m_tree_graph = alloc(graph); + unsigned num_nodes = m_graph.get_num_nodes(); + for (unsigned i = 0; i < num_nodes; ++i) { + m_tree_graph->init_var(i); + } + + vector const & es = m_graph.get_all_edges(); + svector::const_iterator it = m_tree.begin(), end = m_tree.end(); + for(; it != end; ++it) { + edge const & e = es[*it]; + if (leave_id != *it) { + m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); + } + } + edge const & e = es[enter_id]; + m_tree_graph->add_edge(e.get_source(), e.get_target(), e.get_weight(), explanation()); + + node_id root = num_nodes - 1; + m_tree_graph->bfs_undirected(root, m_pred, m_depth); + m_tree_graph->dfs_undirected(root, m_thread); + + vector const & tree_edges = m_tree_graph->get_all_edges(); + for (unsigned i = 0; i < tree_edges.size(); ++i) { + edge const & e = tree_edges[i]; + dl_var src = e.get_source(); + dl_var tgt = e.get_target(); + edge_id id; + VERIFY(m_graph.get_edge_id(src, tgt, id)); + SASSERT(tgt == m_pred[src] || src == m_pred[tgt]); + if (tgt == m_pred[src]) { + m_tree[src] = id; + } + else { + m_tree[tgt] = id; + } + } + + node_id p = m_graph.get_source(enter_id); + node_id q = m_graph.get_target(enter_id); + m_root_t2 = p == m_pred[q] ? q : p; + } + +} + +#endif diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index 95b150a8c..a83b4a035 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -22,6 +22,7 @@ Notes: #include"smt_params.h" #include"smt_params_helper.hpp" #include"rewriter_types.h" +#include"filter_model_converter.h" class smt_tactic : public tactic { smt_params m_params; @@ -150,6 +151,7 @@ public: scoped_ptr dep2bool; scoped_ptr bool2dep; ptr_vector assumptions; + ref fmc; if (in->unsat_core_enabled()) { if (in->proofs_enabled()) throw tactic_exception("smt tactic does not support simultaneous generation of proofs and unsat cores"); @@ -191,6 +193,10 @@ public: dep2bool->insert(d, b); bool2dep->insert(b, d); assumptions.push_back(b); + if (!fmc) { + fmc = alloc(filter_model_converter, m); + } + fmc->insert(to_app(b)->get_decl()); } clause.push_back(m.mk_not(b)); } @@ -229,11 +235,12 @@ public: // the empty assertion set is trivially satifiable. in->reset(); result.push_back(in.get()); - // store the model in a do nothin model converter. + // store the model in a no-op model converter, and filter fresh Booleans if (in->models_enabled()) { model_ref md; m_ctx->get_model(md); mc = model2model_converter(md.get()); + mc = concat(fmc.get(), mc.get()); } pc = 0; core = 0; diff --git a/src/smt/theory_arith.cpp b/src/smt/theory_arith.cpp index 0bb356b95..9a7d08151 100644 --- a/src/smt/theory_arith.cpp +++ b/src/smt/theory_arith.cpp @@ -25,5 +25,6 @@ namespace smt { template class theory_arith; // template class theory_arith; // template class theory_arith; - + + template class smt::theory_arith; }; diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index d1af762bb..6531c33f2 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -36,6 +36,8 @@ Revision History: #include"grobner.h" #include"arith_simplifier_plugin.h" #include"arith_eq_solver.h" +#include"theory_opt.h" +#include"uint_set.h" namespace smt { @@ -80,7 +82,7 @@ namespace smt { */ template - class theory_arith : public theory, private Ext { + class theory_arith : public theory, public theory_opt, private Ext { public: typedef typename Ext::numeral numeral; typedef typename Ext::inf_numeral inf_numeral; @@ -90,6 +92,7 @@ namespace smt { static const int dead_row_id = -1; protected: bool proofs_enabled() const { return get_manager().proofs_enabled(); } + bool coeffs_enabled() const { return proofs_enabled() || m_bound_watch != null_bool_var; } struct linear_monomial { numeral m_coeff; @@ -242,6 +245,8 @@ namespace smt { parameter* params(char const* name); }; + class gomory_cut_justification; + class bound { protected: theory_var m_var; @@ -262,6 +267,7 @@ namespace smt { inf_numeral const & get_value() const { return m_value; } virtual bool has_justification() const { return false; } virtual void push_justification(antecedents& antecedents, numeral const& coeff, bool proofs_enabled) {} + virtual void display(theory_arith const& th, std::ostream& out) const; }; @@ -273,14 +279,14 @@ namespace smt { class atom : public bound { protected: bool_var m_bvar; - numeral m_k; + inf_numeral m_k; unsigned m_atom_kind:2; // atom kind unsigned m_is_true:1; // cache: true if the atom was assigned to true. public: - atom(bool_var bv, theory_var v, numeral const & k, atom_kind kind); + atom(bool_var bv, theory_var v, inf_numeral const & k, atom_kind kind); atom_kind get_atom_kind() const { return static_cast(m_atom_kind); } virtual ~atom() {} - numeral const & get_k() const { return m_k; } + inline inf_numeral const & get_k() const { return m_k; } bool_var get_bool_var() const { return m_bvar; } bool is_true() const { return m_is_true; } void assign_eh(bool is_true, inf_numeral const & epsilon); @@ -288,6 +294,7 @@ namespace smt { virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { a.push_lit(literal(get_bool_var(), !m_is_true), coeff, proofs_enabled); } + virtual void display(theory_arith const& th, std::ostream& out) const; }; class eq_bound : public bound { @@ -306,6 +313,7 @@ namespace smt { SASSERT(m_lhs->get_root() == m_rhs->get_root()); a.push_eq(enode_pair(m_lhs, m_rhs), coeff, proofs_enabled); } + virtual void display(theory_arith const& th, std::ostream& out) const; }; class derived_bound : public bound { @@ -320,6 +328,7 @@ namespace smt { virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled); virtual void push_lit(literal l, numeral const&) { m_lits.push_back(l); } virtual void push_eq(enode_pair const& p, numeral const&) { m_eqs.push_back(p); } + virtual void display(theory_arith const& th, std::ostream& out) const; }; class justified_derived_bound : public derived_bound { @@ -433,6 +442,7 @@ namespace smt { bool m_eager_gcd; // true if gcd should be applied at every add_row unsigned m_final_check_idx; + // backtracking svector m_bound_trail; svector m_unassigned_atoms_trail; @@ -869,13 +879,28 @@ namespace smt { row m_tmp_row; void add_tmp_row(row & r1, numeral const & coeff, row const & r2); - theory_var pick_var_to_leave(theory_var x_j, bool inc, numeral & a_ij, inf_numeral & gain); + theory_var pick_var_to_leave(bool has_int, theory_var x_j, bool inc, numeral & a_ij, inf_numeral & gain, bool& skiped_row); + bool is_safe_to_leave(theory_var x, bool inc, bool& has_int, bool& is_shared); template void add_tmp_row_entry(row & r, numeral const & coeff, theory_var v); - bool max_min(row & r, bool max); - bool max_min(theory_var v, bool max); + enum max_min_t { UNBOUNDED, AT_BOUND, OPTIMIZED, BEST_EFFORT}; + max_min_t max_min(theory_var v, bool max, bool maintain_integrality, bool& has_shared); + bool has_interface_equality(theory_var v); bool max_min(svector const & vars); + max_min_t max_min(row& r, bool max, bool maintain_integrality, bool& has_shared); + bool unbounded_gain(inf_numeral const & max_gain) const; + bool safe_gain(inf_numeral const& min_gain, inf_numeral const & max_gain) const; + void normalize_gain(numeral const& divisor, inf_numeral & max_gain) const; + void init_gains(theory_var x, bool inc, inf_numeral& min_gain, inf_numeral& max_gain); + bool update_gains(bool inc, theory_var x_i, numeral const& a_ij, + inf_numeral& min_gain, inf_numeral& max_gain); + bool move_to_bound(theory_var x_i, bool inc, unsigned& best_efforts, bool& has_shared); + bool pick_var_to_leave( + theory_var x_j, bool inc, numeral & a_ij, + inf_numeral& min_gain, inf_numeral& max_gain, + bool& shared, theory_var& x_i); + // ----------------------------------- // // Non linear @@ -1004,6 +1029,27 @@ namespace smt { virtual bool get_value(enode * n, expr_ref & r); + // ----------------------------------- + // + // Optimization + // + // ----------------------------------- + virtual inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared); + virtual inf_eps_rational value(theory_var v); + virtual theory_var add_objective(app* term); + virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val); + void enable_record_conflict(expr* bound); + void record_conflict(unsigned num_lits, literal const * lits, + unsigned num_eqs, enode_pair const * eqs, + unsigned num_params, parameter* params); + inf_eps_rational conflict_minimize(); + private: + virtual expr_ref mk_gt(theory_var v); + + bool_var m_bound_watch; + inf_eps_rational m_upper_bound; + bool get_theory_vars(expr * n, uint_set & vars); + public: // ----------------------------------- // // Pretty Printing @@ -1034,6 +1080,8 @@ namespace smt { void display_bounds_in_smtlib() const; void display_nl_monomials(std::ostream & out) const; void display_coeff_exprs(std::ostream & out, sbuffer const & p) const; + void display_interval(std::ostream& out, interval const& i); + void display_deps(std::ostream& out, v_dependency* dep); protected: // ----------------------------------- @@ -1049,9 +1097,11 @@ namespace smt { bool wf_rows() const; bool wf_column(theory_var v) const; bool wf_columns() const; + bool valid_assignment() const; bool valid_row_assignment() const; bool valid_row_assignment(row const & r) const; bool satisfy_bounds() const; + bool satisfy_integrality() const; #endif }; @@ -1071,6 +1121,7 @@ namespace smt { static inf_numeral mk_inf_numeral(numeral const & n, numeral const & r) { return inf_numeral(n, r); } + static bool is_infinite(inf_numeral const& ) { return false; } mi_ext() : m_int_epsilon(rational(1)), m_real_epsilon(rational(0), true) {} }; @@ -1087,6 +1138,8 @@ namespace smt { UNREACHABLE(); return inf_numeral(n); } + static bool is_infinite(inf_numeral const& ) { return false; } + i_ext() : m_int_epsilon(1), m_real_epsilon(1) {} }; @@ -1103,6 +1156,8 @@ namespace smt { UNREACHABLE(); return inf_numeral(n); } + static bool is_infinite(inf_numeral const& ) { return false; } + si_ext(): m_int_epsilon(s_integer(1)), m_real_epsilon(s_integer(1)) {} }; @@ -1123,13 +1178,41 @@ namespace smt { static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) { return inf_numeral(n, i); } + static bool is_infinite(inf_numeral const& ) { return false; } + smi_ext() : m_int_epsilon(s_integer(1)), m_real_epsilon(s_integer(0), true) {} }; + + class inf_ext { + public: + typedef rational numeral; + typedef inf_eps_rational inf_numeral; + inf_numeral m_int_epsilon; + inf_numeral m_real_epsilon; + numeral fractional_part(inf_numeral const& n) { + SASSERT(n.is_rational()); + return n.get_rational() - floor(n); + } + static numeral fractional_part(numeral const & n) { + return n - floor(n); + } + static inf_numeral mk_inf_numeral(numeral const & n, numeral const & r) { + return inf_numeral(inf_rational(n, r)); + } + static bool is_infinite(inf_numeral const& n) { + return !n.get_infinity().is_zero(); + } + + inf_ext() : m_int_epsilon(inf_rational(rational(1))), m_real_epsilon(inf_rational(rational(0), true)) {} + }; + typedef theory_arith theory_mi_arith; typedef theory_arith theory_i_arith; + typedef smt::theory_arith theory_inf_arith; // typedef theory_arith theory_si_arith; // typedef theory_arith theory_smi_arith; + }; diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 21b892e57..8bf366682 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -19,7 +19,11 @@ Revision History: #ifndef _THEORY_ARITH_AUX_H_ #define _THEORY_ARITH_AUX_H_ +#include"inf_eps_rational.h" #include"theory_arith.h" +#include"smt_farkas_util.h" +#include"th_rewriter.h" +#include"filter_model_converter.h" namespace smt { @@ -359,6 +363,19 @@ namespace smt { return m_params.c_ptr(); } + // ----------------------------------- + // + // Bounds + // + // ----------------------------------- + + + template + void theory_arith::bound::display(theory_arith const& th, std::ostream& out) const { + out << "v" << get_var() << " " << get_bound_kind() << " " << get_value(); + } + + // ----------------------------------- // // Atoms @@ -366,7 +383,7 @@ namespace smt { // ----------------------------------- template - theory_arith::atom::atom(bool_var bv, theory_var v, numeral const & k, atom_kind kind): + theory_arith::atom::atom(bool_var bv, theory_var v, inf_numeral const & k, atom_kind kind): bound(v, inf_numeral::zero(), B_LOWER, true), m_bvar(bv), m_k(k), @@ -397,6 +414,27 @@ namespace smt { } } + template + void theory_arith::atom::display(theory_arith const& th, std::ostream& out) const { + literal l(get_bool_var(), !m_is_true); + out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << get_k() << " "; + out << l << ":"; + th.get_context().display_detailed_literal(out, l); + } + + // ----------------------------------- + // + // eq_bound + // + // ----------------------------------- + + template + void theory_arith::eq_bound::display(theory_arith const& th, std::ostream& out) const { + ast_manager& m = th.get_manager(); + out << "#" << m_lhs->get_owner_id() << " " << mk_pp(m_lhs->get_owner(), m) << " = " + << "#" << m_rhs->get_owner_id() << " " << mk_pp(m_rhs->get_owner(), m); + } + // ----------------------------------- // // Auxiliary methods @@ -475,11 +513,11 @@ namespace smt { bool theory_arith::all_coeff_int(row const & r) const { typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead() && !it->m_coeff.is_int()) + for (; it != end; ++it) { + if (!it->is_dead() && !it->m_coeff.is_int()) { TRACE("gomory_cut", display_row(tout, r, true);); return false; - + } } return true; } @@ -707,6 +745,24 @@ namespace smt { } } + template + void theory_arith::derived_bound::display(theory_arith const& th, std::ostream& out) const { + out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << bound::get_value(); + + ast_manager& m = th.get_manager(); + for (unsigned i = 0; i < m_eqs.size(); ++i) { + enode* a = m_eqs[i].first; + enode* b = m_eqs[i].second; + out << " "; + out << "#" << a->get_owner_id() << " " << mk_pp(a->get_owner(), m) << " = " + << "#" << b->get_owner_id() << " " << mk_pp(b->get_owner(), m); + } + for (unsigned i = 0; i < m_lits.size(); ++i) { + literal l = m_lits[i]; + out << " " << l << ":"; th.get_context().display_detailed_literal(out, l); + } + } + template void theory_arith::justified_derived_bound::push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { @@ -901,6 +957,38 @@ namespace smt { r1.reset_var_pos(m_var_pos); } + template + bool theory_arith::is_safe_to_leave(theory_var x, bool inc, bool& has_int, bool& shared) { + + context& ctx = get_context(); + shared |= ctx.is_shared(get_enode(x)); + column & c = m_columns[x]; + typename svector::iterator it = c.begin_entries(); + typename svector::iterator end = c.end_entries(); + has_int = false; + bool unbounded = (inc && !upper(x)) || (!inc && !lower(x)); + bool was_unsafe = false; + for (; it != end; ++it) { + if (it->is_dead()) continue; + row const & r = m_rows[it->m_row_id]; + theory_var s = r.get_base_var(); + numeral const & coeff = r[it->m_row_idx].m_coeff; + if (s != null_theory_var && is_int(s)) has_int = true; + bool is_unsafe = (s != null_theory_var && is_int(s) && !coeff.is_int()); + shared |= (s != null_theory_var && ctx.is_shared(get_enode(s))); + was_unsafe |= is_unsafe; + bool inc_s = coeff.is_neg() ? inc : !inc; + unbounded &= !get_bound(s, inc_s); + TRACE("opt", tout << "is v" << x << " safe to leave for v" << s + << "? " << (is_unsafe?"no":"yes") << " " << (has_int?"int":"real") << " " << (unbounded?"unbounded":"bounded") << "\n"; + display_row(tout, r, true);); + if (was_unsafe && !unbounded) return false; + } + + return !was_unsafe || unbounded; + } + + /** \brief Select tightest variable x_i to pivot with x_j. The goal is to select a x_i such that the value of x_j is increased @@ -912,10 +1000,22 @@ namespace smt { If no x_i imposes a restriction on x_j, then return null_theory_var. That is, x_j is free to move to its upper bound (lower bound). + + Get the equations for x_j: + + x_i1 = coeff_1 * x_j + rest_1 + ... + x_in = coeff_n * x_j + rest_n + + gain_k := (upper_bound(x_ik) - value(x_ik))/coeff_k + */ + template - theory_var theory_arith::pick_var_to_leave(theory_var x_j, bool inc, numeral & a_ij, inf_numeral & gain) { - TRACE("maximize", tout << "selecting variable to replace v" << x_j << ", inc: " << inc << "\n";); + theory_var theory_arith::pick_var_to_leave( + bool has_int, theory_var x_j, bool inc, + numeral & a_ij, inf_numeral & gain, bool& skipped_row) { + TRACE("opt", tout << "selecting variable to replace v" << x_j << ", inc: " << inc << "\n";); theory_var x_i = null_theory_var; inf_numeral curr_gain; column & c = m_columns[x_j]; @@ -936,146 +1036,761 @@ namespace smt { if (curr_gain.is_neg()) curr_gain.neg(); if (x_i == null_theory_var || (curr_gain < gain) || (gain.is_zero() && curr_gain.is_zero() && s < x_i)) { + if (is_int(s) && !curr_gain.is_int()) { + skipped_row = true; + continue; + } + if (is_int(x_j) && !curr_gain.is_int()) { + skipped_row = true; + continue; + } + if (!curr_gain.is_int() && has_int) { + skipped_row = true; + continue; + } x_i = s; a_ij = coeff; gain = curr_gain; + TRACE("opt", + tout << "x_i: v" << x_i << ", gain: " << gain << "\n"; + tout << "value(s): (" << get_value(s) << " - " << b->get_value() << ")/" << coeff << "\n"; + display_row(tout, r, true); + ); } } } - TRACE("maximize", tout << "x_j: v" << x_i << ", gain: " << gain << "\n";); + TRACE("opt", tout << "x_i: v" << x_i << ", gain: " << gain << "\n";); } } - TRACE("maximize", tout << "x_i v" << x_i << "\n";); + TRACE("opt", tout << "x_i v" << x_i << "\n";); return x_i; } + template + bool theory_arith::get_theory_vars(expr * n, uint_set & vars) { + rational r; + expr* x, *y; + if (m_util.is_numeral(n, r)) { + return true; + } + else if (m_util.is_add(n)) { + for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { + if (!get_theory_vars(to_app(n)->get_arg(i), vars)) { + return false; + } + } + } + else if (m_util.is_to_real(n, x) || m_util.is_to_int(n, x)) { + return get_theory_vars(x, vars); + } + else if (m_util.is_mul(n, x, y) && m_util.is_numeral(x, r)) { + return get_theory_vars(y, vars); + } + else if (m_util.is_mul(n, y, x) && m_util.is_numeral(x, r)) { + return get_theory_vars(y, vars); + } + else if (!is_app(n)) { + return false; + } + else if (to_app(n)->get_family_id() == m_util.get_family_id()) { + return false; + } + else { + context & ctx = get_context(); + SASSERT(ctx.e_internalized(n)); + enode * e = ctx.get_enode(n); + if (is_attached_to_var(e)) { + vars.insert(e->get_th_var(get_id())); + } + return true; + } + return true; + } + + // + // add_objective(expr* term) internalizes the arithmetic term and creates + // a row for it if it is not already internalized. + // Then return the variable corresponding to the term. + // + + template + theory_var theory_arith::add_objective(app* term) { + theory_var v = internalize_term_core(term); + TRACE("opt", tout << mk_pp(term, get_manager()) << " |-> v" << v << "\n";); + SASSERT(!is_quasi_base(v)); + if (!is_linear(get_manager(), term)) { + v = null_theory_var; + } + return v; + } + + template + inf_eps_rational theory_arith::value(theory_var v) { + return inf_eps_rational(get_value(v)); + } + + template + inf_eps_rational theory_arith::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + TRACE("bound_bug", display_var(tout, v); display(tout);); + has_shared = false; + max_min_t r = max_min(v, true, true, has_shared); + if (r == UNBOUNDED) { + has_shared = false; + blocker = get_manager().mk_false(); + return inf_eps_rational::infinity(); + } + else { + blocker = mk_gt(v); + return inf_eps_rational(get_value(v)); + } + + } + + /** + \brief: Create an atom that enforces the inequality v > val + The arithmetical expression encoding the inequality suffices + for the theory of aritmetic. + */ + template + expr_ref theory_arith::mk_gt(theory_var v) { + ast_manager& m = get_manager(); + inf_numeral const& val = get_value(v); + expr* obj = get_enode(v)->get_owner(); + expr_ref e(m); + rational r = val.get_rational(); + if (m_util.is_int(m.get_sort(obj))) { + if (r.is_int()) { + r += rational::one(); + } + else { + r = ceil(r); + } + e = m_util.mk_numeral(r, m.get_sort(obj)); + e = m_util.mk_ge(obj, e); + } + else { + // obj is over the reals. + e = m_util.mk_numeral(r, m.get_sort(obj)); + + if (val.get_infinitesimal().is_neg()) { + e = m_util.mk_ge(obj, e); + } + else { + e = m_util.mk_gt(obj, e); + } + } + return e; + } + + /** + \brief create atom that enforces: val <= v + The atom that enforces the inequality is created directly + as opposed to using arithmetical terms. + This allows to handle inequalities with non-standard numbers. + */ + template + expr_ref theory_arith::mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val) { + ast_manager& m = get_manager(); + context& ctx = get_context(); + std::ostringstream strm; + strm << val << " <= " << mk_pp(get_enode(v)->get_owner(), get_manager()); + app* b = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); + if (!ctx.b_internalized(b)) { + fm.insert(b->get_decl()); + bool_var bv = ctx.mk_bool_var(b); + ctx.set_var_theory(bv, get_id()); + // ctx.set_enode_flag(bv, true); + atom* a = alloc(atom, bv, v, val, A_LOWER); + mk_bound_axioms(a); + m_unassigned_atoms[v]++; + m_var_occs[v].push_back(a); + m_atoms.push_back(a); + insert_bv2a(bv, a); + TRACE("arith", tout << mk_pp(b, m) << "\n"; + display_atom(tout, a, false);); + } + return expr_ref(b, m); + } + + + /** + \brief enable watching bound atom. + */ + template + void theory_arith::enable_record_conflict(expr* bound) { + m_params.m_arith_bound_prop = BP_NONE; + SASSERT(propagation_mode() == BP_NONE); // bound propagtion rules are not (yet) handled. + if (bound) { + context& ctx = get_context(); + m_bound_watch = ctx.get_bool_var(bound); + } + else { + m_bound_watch = null_bool_var; + } + m_upper_bound = -inf_eps_rational::infinity(); + } + + /** + \brief + pos < 0 + == + r(Ax <= b) + q(v <= val) + == + val' <= q*v & q*v <= q*val + + q*v - val' >= 0 + + => + (q*v - val' - q*v)/q >= -v + == + val/q <= v + */ + + template + void theory_arith::record_conflict( + unsigned num_lits, literal const * lits, + unsigned num_eqs, enode_pair const * eqs, + unsigned num_params, parameter* params) { + ast_manager& m = get_manager(); + context& ctx = get_context(); + expr_ref tmp(m), vq(m); + expr* x, *y, *e; + if (null_bool_var == m_bound_watch) { + return; + } + unsigned idx = num_lits; + for (unsigned i = 0; i < num_lits; ++i) { + if (m_bound_watch == lits[i].var()) { + //SASSERT(!lits[i].sign()); + idx = i; + break; + } + } + if (idx == num_lits) { + return; + } + for (unsigned i = 0; i < num_lits; ++i) { + ctx.literal2expr(lits[i], tmp); + } + for (unsigned i = 0; i < num_eqs; ++i) { + enode_pair const& p = eqs[i]; + x = p.first->get_owner(); + y = p.second->get_owner(); + tmp = m.mk_eq(x,y); + } + + SASSERT(num_params == 1 + num_lits + num_eqs); + SASSERT(params[0].is_symbol()); + SASSERT(params[0].get_symbol() == symbol("farkas")); // for now, just handle this rule. + farkas_util farkas(m); + rational q; + for (unsigned i = 0; i < num_lits; ++i) { + parameter const& pa = params[i+1]; + SASSERT(pa.is_rational()); + if (idx == i) { + q = abs(pa.get_rational()); + continue; + } + ctx.literal2expr(lits[i], tmp); + farkas.add(abs(pa.get_rational()), to_app(tmp)); + } + for (unsigned i = 0; i < num_eqs; ++i) { + enode_pair const& p = eqs[i]; + x = p.first->get_owner(); + y = p.second->get_owner(); + tmp = m.mk_eq(x,y); + parameter const& pa = params[1 + num_lits + i]; + SASSERT(pa.is_rational()); + farkas.add(abs(pa.get_rational()), to_app(tmp)); + } + tmp = farkas.get(); + // IF_VERBOSE(1, verbose_stream() << "Farkas result: " << tmp << "\n";); + atom* a = get_bv2a(m_bound_watch); + SASSERT(a); + expr_ref_vector terms(m); + vector mults; + bool strict = false; + if (m_util.is_le(tmp, x, y) || m_util.is_ge(tmp, y, x)) { + } + else if (m.is_not(tmp, e) && (m_util.is_le(e, y, x) || m_util.is_ge(e, x, y))) { + strict = true; + } + else if (m.is_eq(tmp, x, y)) { + } + else { + UNREACHABLE(); + } + e = var2expr(a->get_var()); + q *= farkas.get_normalize_factor(); + SASSERT(!m_util.is_int(e) || q.is_int()); // TBD: not fully handled. + if (q.is_one()) { + vq = e; + } + else { + vq = m_util.mk_mul(m_util.mk_numeral(q, q.is_int()), e); + } + vq = m_util.mk_add(m_util.mk_sub(x, y), vq); + if (!q.is_one()) { + vq = m_util.mk_div(vq, m_util.mk_numeral(q, q.is_int())); + } + th_rewriter rw(m); + rw(vq, tmp); + VERIFY(m_util.is_numeral(tmp, q)); + if (m_upper_bound < q) { + m_upper_bound = q; + if (strict) { + m_upper_bound -= get_epsilon(a->get_var()); + } + IF_VERBOSE(1, verbose_stream() << "new upper bound: " << m_upper_bound << "\n";); + } + } + + /** + \brief find the minimal upper bound on the variable that was last enabled + for conflict recording. + */ + template + inf_eps_rational theory_arith::conflict_minimize() { + return m_upper_bound; + } + + + /** + \brief Select tightest variable x_i to pivot with x_j. The goal + is to select a x_i such that the value of x_j is increased + (decreased) if inc = true (inc = false), and the tableau + remains feasible. Store the gain in x_j of the pivoting + operation in 'gain'. Note the gain can be too much. That is, + it may make x_i infeasible. In this case, instead of pivoting + we move x_j to its upper bound (lower bound) when inc = true (inc = false). + + If no x_i imposes a restriction on x_j, then return null_theory_var. + That is, x_j is free to move to its upper bound (lower bound). + + Get the equations for x_j: + + x_i1 = coeff_1 * x_j + rest_1 + ... + x_in = coeff_n * x_j + rest_n + + gain_k := (upper_bound(x_ik) - value(x_ik))/coeff_k + + */ + + + template + bool theory_arith::pick_var_to_leave( + theory_var x_j, // non-base variable to increment/decrement + bool inc, + numeral & a_ij, // coefficient of x_i + inf_numeral& min_gain, // minimal required gain on x_j (integral value on integers) + inf_numeral& max_gain, // maximal possible gain on x_j + bool& has_shared, // determine if pivot involves shared variable + theory_var& x_i) { // base variable to pivot with x_j + + x_i = null_theory_var; + context& ctx = get_context(); + column & c = m_columns[x_j]; + typename svector::iterator it = c.begin_entries(); + typename svector::iterator end = c.end_entries(); + init_gains(x_j, inc, min_gain, max_gain); + has_shared |= ctx.is_shared(get_enode(x_j)); + for (; it != end; ++it) { + if (it->is_dead()) continue; + row const & r = m_rows[it->m_row_id]; + theory_var s = r.get_base_var(); + numeral const & coeff_ij = r[it->m_row_idx].m_coeff; + if (update_gains(inc, s, coeff_ij, min_gain, max_gain) || + (x_i == null_theory_var && !unbounded_gain(max_gain))) { + x_i = s; + a_ij = coeff_ij; + } + has_shared |= ctx.is_shared(get_enode(s)); + } + bool empty_column = (c.begin_entries() == end); + TRACE("opt", + tout << (safe_gain(min_gain, max_gain)?"safe":"unsafe") << "\n"; + tout << "min gain: " << min_gain; + tout << " max gain: " << max_gain << "\n"; + tout << "v" << x_i << " "; + tout << (has_shared?"shared":"not shared") << "\n";); + + SASSERT(!safe_gain(min_gain, max_gain) || + empty_column || + (unbounded_gain(max_gain) == (x_i == null_theory_var))); + + return !empty_column && safe_gain(min_gain, max_gain); + } + + template + bool theory_arith::unbounded_gain(inf_numeral const & max_gain) const { + return max_gain.is_minus_one(); + } + + /* + A gain is 'safe' with respect to the tableau if: + - the selected variable is unbounded and every base variable where it occurs is unbounded + in the direction of the gain. max_gain == -1 is used to indicate unbounded variables. + - the selected variable is a rational (min_gain == -1, max_gain >= 0). + - + */ + template + bool theory_arith::safe_gain(inf_numeral const& min_gain, inf_numeral const & max_gain) const { + return + unbounded_gain(max_gain) || + min_gain <= max_gain; + } + + /** + \brief ensure that maximal gain is divisible by divisor. + */ + template + void theory_arith::normalize_gain(numeral const& divisor, inf_numeral & max_gain) const { + SASSERT(divisor.is_int()); + SASSERT(divisor.is_pos()); + if (!divisor.is_one() && !max_gain.is_minus_one()) { + max_gain = floor(max_gain/divisor)*divisor; + } + } + + /** + \brief initialize gains for x_j based on the bounds for x_j. + */ + template + void theory_arith::init_gains( + theory_var x, // non-base variable to increment/decrement + bool inc, + inf_numeral& min_gain, // min value to increment, -1 if rational + inf_numeral& max_gain) { // max value to decrement, -1 if unbounded + min_gain = -inf_numeral::one(); + max_gain = -inf_numeral::one(); + if (inc && upper(x)) { + max_gain = upper_bound(x) - get_value(x); + } + else if (!inc && lower(x)) { + max_gain = get_value(x) - lower_bound(x); + } + if (is_int(x)) { + min_gain = inf_numeral::one(); + } + SASSERT(max_gain.is_minus_one() || !max_gain.is_neg()); + SASSERT(min_gain.is_minus_one() || min_gain.is_one()); + SASSERT(!is_int(x) || max_gain.is_int()); + SASSERT(is_int(x) == min_gain.is_one()); + TRACE("opt", + tout << "v" << x << " " + << "min gain: " << min_gain << " " + << "max gain: " << max_gain << "\n";); + + } + + template + bool theory_arith::update_gains( + bool inc, // increment/decrement x_j + theory_var x_i, // potential base variable to pivot + numeral const& a_ij, // coefficient of x_j in row where x_i is base. + inf_numeral& min_gain, // min value to increment, -1 if rational + inf_numeral& max_gain) { // max value to decrement, -1 if unbounded + + // x_i = row + a_ij*x_j + // a_ij > 0, inc -> decrement x_i + // a_ij < 0, !inc -> decrement x_i + // a_ij denominator + + if (!safe_gain(min_gain, max_gain)) return false; + + inf_numeral max_inc = inf_numeral::minus_one(); + bool decrement_x_i = (inc && a_ij.is_pos()) || (!inc && a_ij.is_neg()); + if (decrement_x_i && lower(x_i)) { + max_inc = abs((get_value(x_i) - lower_bound(x_i))/a_ij); + } + else if (!decrement_x_i && upper(x_i)) { + max_inc = abs((upper_bound(x_i) - get_value(x_i))/a_ij); + } + numeral den_aij(1); + bool is_tighter = false; + if (is_int(x_i)) den_aij = denominator(a_ij); + SASSERT(den_aij.is_pos() && den_aij.is_int()); + + if (is_int(x_i) && !den_aij.is_one()) { + SASSERT(min_gain.is_pos()); + min_gain = inf_numeral(lcm(min_gain.get_rational(), den_aij)); + normalize_gain(min_gain.get_rational(), max_gain); + } + + if (!max_inc.is_minus_one()) { + if (is_int(x_i)) { + max_inc = floor(max_inc); + normalize_gain(min_gain.get_rational(), max_inc); + } + if (unbounded_gain(max_gain)) { + max_gain = max_inc; + is_tighter = true; + } + else if (max_gain > max_inc) { + max_gain = max_inc; + is_tighter = true; + } + } + TRACE("opt", + tout << "v" << x_i << " a_ij " << a_ij << " " + << "min gain: " << min_gain << " " + << "max gain: " << max_gain << " tighter: " + << (is_tighter?"true":"false") << "\n";); + SASSERT(max_gain.is_minus_one() || !max_gain.is_neg()); + SASSERT(min_gain.is_minus_one() || !min_gain.is_neg()); + SASSERT(!is_int(x_i) || min_gain.is_pos()); + SASSERT(!is_int(x_i) || min_gain.is_int()); + SASSERT(!is_int(x_i) || max_gain.is_int()); + return is_tighter; + } + + /** + \brief Check if bound change affects interface equality. + */ + template + bool theory_arith::has_interface_equality(theory_var x) { + theory_var num = get_num_vars(); + context& ctx = get_context(); + enode* r = get_enode(x)->get_root(); + for (theory_var v = 0; v < num; v++) { + if (v == x) continue; + enode* n = get_enode(v); + if (ctx.is_shared(n) && n->get_root() == r) { + return true; + } + } + return false; + } + + /** \brief Maximize (Minimize) the given temporary row. Return true if succeeded. */ template - bool theory_arith::max_min(row & r, bool max) { - TRACE("max_min", tout << "max_min...\n";); + typename theory_arith::max_min_t theory_arith::max_min( + row & r, + bool max, + bool maintain_integrality, + bool& has_shared) { m_stats.m_max_min++; + unsigned best_efforts = 0; + bool inc = false; + context& ctx = get_context(); - SASSERT(valid_row_assignment()); - SASSERT(satisfy_bounds()); + SASSERT(!maintain_integrality || valid_assignment()); - theory_var x_i = null_theory_var; - theory_var x_j = null_theory_var; - bool inc = false; numeral a_ij, curr_a_ij, coeff, curr_coeff; - inf_numeral curr_gain, gain; + inf_numeral min_gain, max_gain, curr_min_gain, curr_max_gain; #ifdef _TRACE - unsigned i = 0; + unsigned round = 0; #endif - while (true) { - x_j = null_theory_var; - x_i = null_theory_var; - gain.reset(); - TRACE("maximize", tout << "i: " << i << ", max: " << max << "\n"; display_row(tout, r, true); tout << "state:\n"; display(tout); i++;); + max_min_t result = OPTIMIZED; + has_shared = false; + unsigned max_efforts = 10 + (ctx.get_random_value() % 20); + while (best_efforts < max_efforts) { + theory_var x_j = null_theory_var; + theory_var x_i = null_theory_var; + max_gain.reset(); + min_gain.reset(); + TRACE("opt", tout << "round: " << (round++) << ", max: " << max << "\n"; display_row(tout, r, true); tout << "state:\n"; display(tout);); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { - if (!it->is_dead()) { - theory_var curr_x_j = it->m_var; - SASSERT(is_non_base(curr_x_j)); - curr_coeff = it->m_coeff; - bool curr_inc = curr_coeff.is_pos() ? max : !max; - if ((curr_inc && at_upper(curr_x_j)) || (!curr_inc && at_lower(curr_x_j))) - continue; // variable cannot be used for max/min. - theory_var curr_x_i = pick_var_to_leave(curr_x_j, curr_inc, curr_a_ij, curr_gain); - if (curr_x_i == null_theory_var) { - // we can increase/decrease curr_x_j as much as we want. - x_i = null_theory_var; // unbounded - x_j = curr_x_j; - inc = curr_inc; - break; - } - else if (curr_gain > gain) { - x_i = curr_x_i; - x_j = curr_x_j; - a_ij = curr_a_ij; - coeff = curr_coeff; - gain = curr_gain; - inc = curr_inc; - } - else if (curr_gain.is_zero() && (x_i == null_theory_var || curr_x_i < x_i)) { - x_i = curr_x_i; - x_j = curr_x_j; - a_ij = curr_a_ij; - coeff = curr_coeff; - gain = curr_gain; - inc = curr_inc; - // continue - } + if (it->is_dead()) continue; + theory_var curr_x_j = it->m_var; + theory_var curr_x_i = null_theory_var; + SASSERT(is_non_base(curr_x_j)); + curr_coeff = it->m_coeff; + bool curr_inc = curr_coeff.is_pos() ? max : !max; + if ((curr_inc && at_upper(curr_x_j)) || (!curr_inc && at_lower(curr_x_j))) { + // variable cannot be used for max/min. + continue; + } + if (!pick_var_to_leave(curr_x_j, curr_inc, curr_a_ij, + curr_min_gain, curr_max_gain, + has_shared, curr_x_i)) { + best_efforts++; + } + else { + SASSERT(safe_gain(curr_min_gain, curr_max_gain)); + } + if (curr_x_i == null_theory_var) { + TRACE("opt", tout << "unbounded\n";); + // we can increase/decrease curr_x_j as much as we want. + x_i = null_theory_var; // unbounded + x_j = curr_x_j; + inc = curr_inc; + min_gain = curr_min_gain; + max_gain = curr_max_gain; + break; + } + else if (curr_max_gain > max_gain) { + x_i = curr_x_i; + x_j = curr_x_j; + a_ij = curr_a_ij; + coeff = curr_coeff; + max_gain = curr_max_gain; + min_gain = curr_min_gain; + inc = curr_inc; + } + else if (curr_max_gain.is_zero() && (x_i == null_theory_var || curr_x_i < x_i)) { + x_i = curr_x_i; + x_j = curr_x_j; + a_ij = curr_a_ij; + coeff = curr_coeff; + max_gain = curr_max_gain; + min_gain = curr_min_gain; + inc = curr_inc; + // continue } } - TRACE("maximize", tout << "after traversing row:\nx_i: v" << x_i << ", x_j: v" << x_j << ", gain: " << gain << "\n";); + TRACE("opt", tout << "after traversing row:\nx_i: v" << x_i << ", x_j: v" << x_j << ", gain: " << max_gain << "\n"; + tout << "best efforts: " << best_efforts << "\n"; + display(tout);); + if (x_j == null_theory_var) { - TRACE("maximize", tout << "row is " << (max ? "maximized" : "minimized") << "\n";); - SASSERT(valid_row_assignment()); - SASSERT(satisfy_bounds()); - return true; + TRACE("opt", tout << "row is " << (max ? "maximized" : "minimized") << "\n";); + SASSERT(!maintain_integrality || valid_assignment()); + result = OPTIMIZED; + break; + } + + if (min_gain.is_pos() && !min_gain.is_one()) { + ++best_efforts; } - if (x_i == null_theory_var) { // can increase/decrease x_j as much as we want. + if (inc && upper(x_j)) { - update_value(x_j, upper_bound(x_j) - get_value(x_j)); - TRACE("maximize", tout << "moved v" << x_j << " to upper bound\n";); - SASSERT(valid_row_assignment()); - SASSERT(satisfy_bounds()); + SASSERT(!unbounded_gain(max_gain)); + update_value(x_j, max_gain); + TRACE("opt", tout << "moved v" << x_j << " to upper bound\n";); + SASSERT(!maintain_integrality || valid_assignment()); continue; } if (!inc && lower(x_j)) { - update_value(x_j, lower_bound(x_j) - get_value(x_j)); - TRACE("maximize", tout << "moved v" << x_j << " to lower bound\n";); - SASSERT(valid_row_assignment()); - SASSERT(satisfy_bounds()); + SASSERT(!unbounded_gain(max_gain)); + max_gain.neg(); + update_value(x_j, max_gain); + TRACE("opt", tout << "moved v" << x_j << " to lower bound\n";); + SASSERT(!maintain_integrality || valid_assignment()); continue; } - return false; // unbounded. - } - - if (!is_fixed(x_j) && is_bounded(x_j) && (upper_bound(x_j) - lower_bound(x_j) <= gain)) { - // can increase/decrease x_j up to upper/lower bound. - if (inc) { - update_value(x_j, upper_bound(x_j) - get_value(x_j)); - TRACE("maximize", tout << "moved v" << x_j << " to upper bound\n";); +#if 0 + if (ctx.is_shared(get_enode(x_j)) && has_interface_equality(x_j)) { + ++best_efforts; } else { - update_value(x_j, lower_bound(x_j) - get_value(x_j)); - TRACE("maximize", tout << "moved v" << x_j << " to lower bound\n";); + SASSERT(unbounded_gain(max_gain)); + has_shared = false; + best_efforts = 0; } - SASSERT(valid_row_assignment()); - SASSERT(satisfy_bounds()); +#endif + // + // NB. As it stands this is a possibly unsound conclusion for shared theories. + // the tradeoff is non-termination for unbounded objectives in the + // presence of sharing. + // + has_shared = false; + best_efforts = 0; + result = UNBOUNDED; + break; + } + + if (!is_fixed(x_j) && is_bounded(x_j) && + (upper_bound(x_j) - lower_bound(x_j) == max_gain)) { + // can increase/decrease x_j up to upper/lower bound. + if (inc) { + TRACE("opt", tout << "moved v" << x_j << " to upper bound\n";); + } + else { + max_gain.neg(); + TRACE("opt", tout << "moved v" << x_j << " to lower bound\n";); + } + update_value(x_j, max_gain); + SASSERT(!maintain_integrality || valid_assignment()); continue; } - - TRACE("maximize", tout << "max: " << max << ", x_i: v" << x_i << ", x_j: v" << x_j << ", a_ij: " << a_ij << ", coeff: " << coeff << "\n";); - bool move_xi_to_lower; - if (inc) - move_xi_to_lower = a_ij.is_pos(); - else - move_xi_to_lower = a_ij.is_neg(); + + TRACE("opt", tout << "max: " << max << ", x_i: v" << x_i << ", x_j: v" << x_j << ", a_ij: " << a_ij << ", coeff: " << coeff << "\n"; + if (upper(x_i)) tout << "upper x_i: " << upper_bound(x_i) << " "; + if (lower(x_i)) tout << "lower x_i: " << lower_bound(x_i) << " "; + tout << "value x_i: " << get_value(x_i) << "\n"; + if (upper(x_j)) tout << "upper x_j: " << upper_bound(x_j) << " "; + if (lower(x_j)) tout << "lower x_j: " << lower_bound(x_j) << " "; + tout << "value x_j: " << get_value(x_j) << "\n"; + ); pivot(x_i, x_j, a_ij, false); + SASSERT(is_non_base(x_i)); SASSERT(is_base(x_j)); - if (move_xi_to_lower) - update_value(x_i, lower_bound(x_i) - get_value(x_i)); - else - update_value(x_i, upper_bound(x_i) - get_value(x_i)); + + bool inc_xi = inc?a_ij.is_neg():a_ij.is_pos(); + if (!move_to_bound(x_i, inc_xi, best_efforts, has_shared)) { + TRACE("opt", tout << "can't move bound fully\n";); + // break; // break; + + } + row & r2 = m_rows[get_var_row(x_j)]; coeff.neg(); add_tmp_row(r, coeff, r2); SASSERT(r.get_idx_of(x_j) == -1); - SASSERT(valid_row_assignment()); - SASSERT(satisfy_bounds()); + SASSERT(!maintain_integrality || valid_assignment()); } + TRACE("opt", display(tout);); + return (best_efforts>0)?BEST_EFFORT:result; } + /** + Move the variable x_i maximally towards its bound as long as + bounds of other variables are not violated. + Returns false if an integer bound was truncated and no + progress was made. + */ + + template + bool theory_arith::move_to_bound( + theory_var x_i, // variable to move + bool inc, // increment variable or decrement + unsigned& best_efforts, // is bound move a best effort? + bool& has_shared) { // does move include shared variables? + inf_numeral min_gain, max_gain; + init_gains(x_i, inc, min_gain, max_gain); + column & c = m_columns[x_i]; + typename svector::iterator it = c.begin_entries(); + typename svector::iterator end = c.end_entries(); + for (; it != end; ++it) { + if (it->is_dead()) continue; + row const & r = m_rows[it->m_row_id]; + theory_var s = r.get_base_var(); + numeral const & coeff = r[it->m_row_idx].m_coeff; + update_gains(inc, s, coeff, min_gain, max_gain); + has_shared |= get_context().is_shared(get_enode(s)); + } + bool result = false; + if (safe_gain(min_gain, max_gain)) { + TRACE("opt", tout << "Safe delta: " << max_gain << "\n";); + SASSERT(!unbounded_gain(max_gain)); + if (!inc) { + max_gain.neg(); + } + update_value(x_i, max_gain); + if (!min_gain.is_pos() || min_gain.is_one()) { + ++best_efforts; + } + result = !max_gain.is_zero(); + } + if (!result) { + ++best_efforts; + } + return result; + } + + /** \brief Add an entry to a temporary row. @@ -1096,13 +1811,15 @@ namespace smt { \brief Maximize/Minimize the given variable. The bounds of v are update if procedure succeeds. */ template - bool theory_arith::max_min(theory_var v, bool max) { - TRACE("maximize", tout << (max ? "maximizing" : "minimizing") << " v" << v << "...\n";); - SASSERT(valid_row_assignment()); - SASSERT(satisfy_bounds()); + typename theory_arith::max_min_t theory_arith::max_min(theory_var v, bool max, bool maintain_integrality, bool& has_shared) { + expr* e = get_enode(v)->get_owner(); + + SASSERT(!maintain_integrality || valid_assignment()); SASSERT(!is_quasi_base(v)); - if ((max && at_upper(v)) || (!max && at_lower(v))) - return false; // nothing to be done... + if ((max && at_upper(v)) || (!max && at_lower(v))) { + TRACE("opt", tout << "At bound: " << mk_pp(e, get_manager()) << "...\n";); + return AT_BOUND; // nothing to be done... + } m_tmp_row.reset(); if (is_non_base(v)) { add_tmp_row_entry(m_tmp_row, numeral(1), v); @@ -1116,14 +1833,20 @@ namespace smt { add_tmp_row_entry(m_tmp_row, it->m_coeff, it->m_var); } } - if (max_min(m_tmp_row, max)) { - TRACE("maximize", tout << "v" << v << " " << (max ? "max" : "min") << " value is: " << get_value(v) << "\n"; + max_min_t r = max_min(m_tmp_row, max, maintain_integrality, has_shared); + if (r == OPTIMIZED) { + TRACE("opt", tout << mk_pp(e, get_manager()) << " " << (max ? "max" : "min") << " value is: " << get_value(v) << "\n"; display_row(tout, m_tmp_row, true); display_row_info(tout, m_tmp_row);); - mk_bound_from_row(v, get_value(v), max ? B_UPPER : B_LOWER, m_tmp_row); - return true; + mk_bound_from_row(v, get_value(v), max ? B_UPPER : B_LOWER, m_tmp_row); } - return false; + else if (r == UNBOUNDED) { + TRACE("opt", tout << "unbounded: " << mk_pp(e, get_manager()) << "...\n";); + } + else { + TRACE("opt", tout << "not optimized: " << mk_pp(e, get_manager()) << "...\n";); + } + return r; } /** @@ -1133,18 +1856,19 @@ namespace smt { template bool theory_arith::max_min(svector const & vars) { bool succ = false; + bool has_shared = false; svector::const_iterator it = vars.begin(); svector::const_iterator end = vars.end(); for (; it != end; ++it) { - if (max_min(*it, true)) + if (max_min(*it, true, false, has_shared) == OPTIMIZED && !has_shared) succ = true; - if (max_min(*it, false)) + if (max_min(*it, false, false, has_shared) == OPTIMIZED && !has_shared) succ = true; } if (succ) { // process new bounds bool r = propagate_core(); - TRACE("maximize", tout << "after max/min round:\n"; display(tout);); + TRACE("opt", tout << "after max/min round:\n"; display(tout);); return r; } return true; @@ -1250,8 +1974,7 @@ namespace smt { bool theory_arith::try_to_imply_eq(theory_var v1, theory_var v2) { SASSERT(v1 != v2); SASSERT(get_value(v1) == get_value(v2)); - SASSERT(valid_row_assignment()); - SASSERT(satisfy_bounds()); + SASSERT(valid_assignment()); if (is_quasi_base(v1) || is_quasi_base(v2)) return false; m_tmp_row.reset(); @@ -1312,9 +2035,9 @@ namespace smt { m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); - if (max_min(m_tmp_row, true) && + if ((OPTIMIZED == max_min(m_tmp_row, true)) && is_zero_row(m_tmp_row, true, m_tmp_acc_lits, m_tmp_acc_eqs, m_tmp_lit_set, m_tmp_eq_set) && - max_min(m_tmp_row, false) && + (OPTIMIZED == max_min(m_tmp_row, false)) && is_zero_row(m_tmp_row, false, m_tmp_acc_lits, m_tmp_acc_eqs, m_tmp_lit_set, m_tmp_eq_set)) { // v1 == v2 TRACE("imply_eq", tout << "found new implied equality:\n"; diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 3f44e4ab7..65105e65b 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -72,7 +72,9 @@ namespace smt { m_value .push_back(inf_numeral()); } m_old_value .push_back(inf_numeral()); + SASSERT(m_var_occs.size() == static_cast(r)); m_var_occs .push_back(atoms()); + SASSERT(m_var_occs.back().empty()); m_unassigned_atoms .push_back(0); m_var_pos .push_back(-1); m_bounds[0] .push_back(0); @@ -85,10 +87,12 @@ namespace smt { if (is_pure_monomial(n->get_owner())) m_nl_monomials.push_back(r); SASSERT(check_vector_sizes()); + SASSERT(m_var_occs[r].empty()); TRACE("mk_arith_var", tout << "#" << n->get_owner_id() << " :=\n" << mk_ll_pp(n->get_owner(), get_manager()) << "\n"; tout << "is_attached_to_var: " << is_attached_to_var(n) << ", var: " << n->get_th_var(get_id()) << "\n";); get_context().attach_th_var(n, this, r); + SASSERT(m_var_occs.back().empty()); return r; } @@ -435,6 +439,7 @@ namespace smt { j += rational(1); } ctx.mk_th_axiom(get_id(), lits.size(), lits.begin()); + #else // performs slightly worse. literal_buffer lits; @@ -809,6 +814,7 @@ namespace smt { void theory_arith::mk_bound_axioms(atom * a1) { theory_var v = a1->get_var(); atoms & occs = m_var_occs[v]; + TRACE("mk_bound_axioms", tout << "add bound axioms for v" << v << " " << a1 << "\n";); if (!get_context().is_searching()) { // // NB. We make an assumption that user push calls propagation @@ -818,9 +824,9 @@ namespace smt { m_new_atoms.push_back(a1); return; } - numeral const & k1(a1->get_k()); + inf_numeral const & k1(a1->get_k()); atom_kind kind1 = a1->get_atom_kind(); - TRACE("mk_bound_axioms", tout << "making bound axioms for v" << v << " " << kind1 << " " << k1 << "\n";); + TRACE("mk_bound_axioms", display_atom(tout << "making bound axioms for " << a1 << " ", a1, true); tout << "\n";); typename atoms::iterator it = occs.begin(); typename atoms::iterator end = occs.end(); @@ -828,8 +834,14 @@ namespace smt { typename atoms::iterator hi_inf = end, hi_sup = end; for (; it != end; ++it) { atom * a2 = *it; - numeral const & k2(a2->get_k()); + inf_numeral const & k2(a2->get_k()); atom_kind kind2 = a2->get_atom_kind(); + TRACE("mk_bound_axioms", display_atom(tout << "compare " << a2 << " ", a2, true); tout << "\n";); + + if (k1 == k2 && kind1 == kind2) { + continue; + } + SASSERT(k1 != k2 || kind1 != kind2); if (kind2 == A_LOWER) { if (k2 < k1) { @@ -858,15 +870,17 @@ namespace smt { template void theory_arith::mk_bound_axiom(atom* a1, atom* a2) { + TRACE("mk_bound_axioms", tout << a1 << " " << a2 << "\n";); theory_var v = a1->get_var(); literal l1(a1->get_bool_var()); literal l2(a2->get_bool_var()); - numeral const & k1(a1->get_k()); - numeral const & k2(a2->get_k()); + inf_numeral const & k1(a1->get_k()); + inf_numeral const & k2(a2->get_k()); atom_kind kind1 = a1->get_atom_kind(); atom_kind kind2 = a2->get_atom_kind(); bool v_is_int = is_int(v); SASSERT(v == a2->get_var()); + if (k1 == k2 && kind1 == kind2) return; SASSERT(k1 != k2 || kind1 != kind2); parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) }; @@ -887,7 +901,7 @@ namespace smt { else { // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) mk_clause(~l1, ~l2, 3, coeffs); - if (v_is_int && k1 == k2 + numeral(1)) { + if (v_is_int && k1 == k2 + inf_numeral(1)) { // k1 <= x or x <= k1-1 mk_clause(l1, l2, 3, coeffs); } @@ -901,7 +915,7 @@ namespace smt { else { // k1 < k2, k2 <= x => ~(x <= k1) mk_clause(~l1, ~l2, 3, coeffs); - if (v_is_int && k1 == k2 - numeral(1)) { + if (v_is_int && k1 == k2 - inf_numeral(1)) { // x <= k1 or k1+l <= x mk_clause(l1, l2, 3, coeffs); } @@ -997,14 +1011,14 @@ namespace smt { typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { - numeral const & k1(a1->get_k()); + inf_numeral const & k1(a1->get_k()); typename atoms::iterator result = end; found_compatible = false; for (; it != end; ++it) { atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; - numeral const & k2(a2->get_k()); + inf_numeral const & k2(a2->get_k()); found_compatible = true; if (k2 <= k1) { result = it; @@ -1024,13 +1038,13 @@ namespace smt { typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { - numeral const & k1(a1->get_k()); + inf_numeral const & k1(a1->get_k()); found_compatible = false; for (; it != end; ++it) { atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; - numeral const & k2(a2->get_k()); + inf_numeral const & k2(a2->get_k()); found_compatible = true; if (k1 < k2) { return it; @@ -1078,7 +1092,7 @@ namespace smt { ctx.set_var_theory(bv, get_id()); rational _k; VERIFY(m_util.is_numeral(rhs, _k)); - numeral k(_k); + inf_numeral k(_k); atom * a = alloc(atom, bv, v, k, kind); mk_bound_axioms(a); m_unassigned_atoms[v]++; @@ -1086,7 +1100,8 @@ namespace smt { occs.push_back(a); m_atoms.push_back(a); insert_bv2a(bv, a); - TRACE("arith_internalize", tout << "succeeded... v: " << v << " " << kind << " " << k << "\n";); + TRACE("arith_internalize", tout << "succeeded... v" << v << " " << kind << " " << k << "\n"; + for (unsigned i = 0; i + 1 < occs.size(); ++i) tout << occs[i] << "\n";); return true; } @@ -1266,7 +1281,9 @@ namespace smt { result = FC_GIVEUP; break; case FC_CONTINUE: - TRACE("final_check_arith", tout << "continue arith...\n";); + TRACE("final_check_arith", + tout << "continue arith..." + << (get_context().inconsistent()?"inconsistent\n":"\n");); return FC_CONTINUE; } } @@ -1357,6 +1374,9 @@ namespace smt { failed(); return false; } + if (get_context().get_cancel_flag()) { + return true; + } CASSERT("arith", satisfy_bounds()); discard_update_trail(); @@ -1516,7 +1536,8 @@ namespace smt { m_assume_eq_head(0), m_nl_rounds(0), m_nl_gb_exhausted(false), - m_nl_new_exprs(m) { + m_nl_new_exprs(m), + m_bound_watch(null_bool_var) { } template @@ -2055,7 +2076,7 @@ namespace smt { continue; SASSERT(curr_error > inf_numeral(0)); if (best == null_theory_var || (!least && curr_error > best_error) || (least && curr_error < best_error)) { - TRACE("select_pivot", tout << "best: " << best << " v: " << v + TRACE("select_pivot", tout << "best: " << best << " v" << v << ", best_error: " << best_error << ", curr_error: " << curr_error << "\n";); best = v; best_error = curr_error; @@ -2125,6 +2146,9 @@ namespace smt { return false; } TRACE("arith_make_feasible_detail", display(tout);); + if (get_context().get_cancel_flag()) { + return true; + } } TRACE("arith_make_feasible", tout << "make_feasible: sat\n"; display(tout);); CASSERT("arith", wf_rows()); @@ -2181,7 +2205,7 @@ namespace smt { tout << "is_below_lower: " << below_lower(x_i) << ", is_above_upper: " << above_upper(x_i) << "\n";); antecedents& ante = get_antecedents(); explain_bound(r, idx, !is_below, delta, ante); - b->push_justification(ante, numeral(1), proofs_enabled()); + b->push_justification(ante, numeral(1), coeffs_enabled()); set_conflict(ante.lits().size(), ante.lits().c_ptr(), @@ -2324,11 +2348,12 @@ namespace smt { void theory_arith::sign_bound_conflict(bound * b1, bound * b2) { SASSERT(b1->get_var() == b2->get_var()); antecedents& ante = get_antecedents(); - b1->push_justification(ante, numeral(1), proofs_enabled()); - b2->push_justification(ante, numeral(1), proofs_enabled()); + b1->push_justification(ante, numeral(1), coeffs_enabled()); + b2->push_justification(ante, numeral(1), coeffs_enabled()); set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, is_int(b1->get_var()), "farkas"); - TRACE("arith_conflict", tout << "bound conflict\n";); + TRACE("arith_conflict", tout << "bound conflict v" << b1->get_var() << "\n"; + tout << "bounds: " << b1 << " " << b2 << "\n";); } // ----------------------------------- @@ -2584,7 +2609,7 @@ namespace smt { if (!b->has_justification()) continue; if (!relax_bounds() || delta.is_zero()) { - b->push_justification(ante, it->m_coeff, proofs_enabled()); + b->push_justification(ante, it->m_coeff, coeffs_enabled()); continue; } numeral coeff = it->m_coeff; @@ -2611,9 +2636,9 @@ namespace smt { << limit_k1 << " delta: " << delta << " coeff: " << coeff << "\n";); inf_numeral k_2 = k_1; atom * new_atom = 0; - atoms & as = m_var_occs[it->m_var]; - typename atoms::iterator it = as.begin(); - typename atoms::iterator end = as.end(); + atoms const & as = m_var_occs[it->m_var]; + typename atoms::const_iterator it = as.begin(); + typename atoms::const_iterator end = as.end(); for (; it != end; ++it) { atom * a = *it; if (a == b) @@ -2646,7 +2671,7 @@ namespace smt { SASSERT(!is_b_lower || k_2 <= k_1); SASSERT(is_b_lower || k_2 >= k_1); if (new_atom == 0) { - b->push_justification(ante, coeff, proofs_enabled()); + b->push_justification(ante, coeff, coeffs_enabled()); continue; } SASSERT(!is_b_lower || k_2 < k_1); @@ -2660,7 +2685,7 @@ namespace smt { delta -= coeff*(k_2 - k_1); } TRACE("propagate_bounds", tout << "delta (after replace): " << delta << "\n";); - new_atom->push_justification(ante, coeff, proofs_enabled()); + new_atom->push_justification(ante, coeff, coeffs_enabled()); SASSERT(delta >= inf_numeral::zero()); } } @@ -2668,18 +2693,18 @@ namespace smt { template void theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { - atoms & as = m_var_occs[v]; + atoms const & as = m_var_occs[v]; antecedents& ante = get_antecedents(); inf_numeral const & epsilon = get_epsilon(v); inf_numeral delta; - typename atoms::iterator it = as.begin(); - typename atoms::iterator end = as.end(); + typename atoms::const_iterator it = as.begin(); + typename atoms::const_iterator end = as.end(); for (; it != end; ++it) { atom * a = *it; bool_var bv = a->get_bool_var(); literal l(bv); if (get_context().get_assignment(bv) == l_undef) { - numeral const & k2 = a->get_k(); + inf_numeral const & k2 = a->get_k(); delta.reset(); if (a->get_atom_kind() == A_LOWER) { // v >= k k >= k2 |- v >= k2 @@ -2860,13 +2885,13 @@ namespace smt { for (unsigned i = 0; i < num_literals; i++) { ctx.display_detailed_literal(tout, lits[i]); tout << " "; - if (proofs_enabled()) { + if (coeffs_enabled()) { tout << "bound: " << bounds.lit_coeffs()[i] << "\n"; } } for (unsigned i = 0; i < num_eqs; i++) { tout << "#" << eqs[i].first->get_owner_id() << "=#" << eqs[i].second->get_owner_id() << " "; - if (proofs_enabled()) { + if (coeffs_enabled()) { tout << "bound: " << bounds.eq_coeffs()[i] << "\n"; } } @@ -2874,6 +2899,7 @@ namespace smt { tout << bounds.params(proof_rule)[i] << "\n"; } tout << "\n";); + record_conflict(num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification(get_id(), r, num_literals, lits, num_eqs, eqs, @@ -2890,8 +2916,8 @@ namespace smt { typename vector::const_iterator end = r.end_entries(); for (; it != end; ++it) { if (!it->is_dead() && is_fixed(it->m_var)) { - lower(it->m_var)->push_justification(antecedents, it->m_coeff, proofs_enabled()); - upper(it->m_var)->push_justification(antecedents, it->m_coeff, proofs_enabled()); + lower(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); + upper(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); } } } @@ -2994,6 +3020,9 @@ namespace smt { if (!get_context().is_shared(get_enode(v))) continue; inf_numeral const & val = get_value(v); + if (Ext::is_infinite(val)) { + continue; + } rational value = val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational(); theory_var v2; if (mapping.find(value, v2)) { @@ -3216,11 +3245,13 @@ namespace smt { case QUASI_BASE: SASSERT(m_columns[v].size() == 1); del_row(get_var_row(v)); + TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); break; case BASE: SASSERT(lazy_pivoting_lvl() != 0 || m_columns[v].size() == 1); if (lazy_pivoting_lvl() > 0) eliminate(v, false); + TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); del_row(get_var_row(v)); break; case NON_BASE: { @@ -3232,6 +3263,10 @@ namespace smt { pivot(r.get_base_var(), v, r[entry->m_row_idx].m_coeff, false); SASSERT(is_base(v)); del_row(get_var_row(v)); + TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); + } + else { + TRACE("arith_make_feasible", tout << "no row v" << v << "\n";); } break; } } @@ -3250,6 +3285,7 @@ namespace smt { m_bounds[1] .shrink(old_num_vars); SASSERT(check_vector_sizes()); } + SASSERT(m_var_occs.size() == old_num_vars); } template diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 0a70e2645..cc69bb533 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -33,11 +33,15 @@ namespace smt { // Integrality // // ----------------------------------- + /** \brief Move non base variables to one of its bounds. If the variable does not have bounds, it is integer, but it is not assigned to an integer value, then the variable is set to an integer value. + In mixed integer/real problems moving a real variable to a bound could cause an integer value to + have an infinitesimal. Such an assignment would disable mk_gomory_cut, and Z3 would loop. + */ template void theory_arith::move_non_base_vars_to_bounds() { @@ -413,10 +417,10 @@ namespace smt { for (; it != end; ++it) { // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). if (!it->is_dead() && it->m_var != b && (!at_bound(it->m_var) || !get_value(it->m_var).is_rational())) { - TRACE("gomory_cut", tout << "row is gomory cut target:\n"; + TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; display_var(tout, it->m_var); tout << "at_bound: " << at_bound(it->m_var) << "\n"; - tout << "infinitesimal: " << get_value(it->m_var).is_rational() << "\n";); + tout << "infinitesimal: " << !get_value(it->m_var).is_rational() << "\n";); return false; } } @@ -456,13 +460,16 @@ namespace smt { SASSERT(is_well_sorted(get_manager(), result)); } - class gomory_cut_justification : public ext_theory_propagation_justification { + template + class theory_arith::gomory_cut_justification : public ext_theory_propagation_justification { public: - gomory_cut_justification(family_id fid, region & r, + gomory_cut_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, + antecedents& bounds, literal consequent): - ext_theory_propagation_justification(fid, r, num_lits, lits, num_eqs, eqs, consequent) { + ext_theory_propagation_justification(fid, r, num_lits, lits, num_eqs, eqs, consequent, + bounds.num_params(), bounds.params("gomory-cut")) { } // Remark: the assignment must be propagated back to arith virtual theory_id get_from_theory() const { return null_theory_id; } @@ -473,7 +480,8 @@ namespace smt { */ template bool theory_arith::mk_gomory_cut(row const & r) { - SASSERT(!all_coeff_int(r)); + // The following assertion is wrong. It may be violated in mixed-integer problems. + // SASSERT(!all_coeff_int(r)); theory_var x_i = r.get_base_var(); SASSERT(is_int(x_i)); @@ -525,7 +533,7 @@ namespace smt { } // k += new_a_ij * lower_bound(x_j).get_rational(); k.addmul(new_a_ij, lower_bound(x_j).get_rational()); - lower(x_j)->push_justification(ante, numeral::zero(), proofs_enabled()); + lower(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } else { SASSERT(at_upper(x_j)); @@ -541,7 +549,7 @@ namespace smt { } // k += new_a_ij * upper_bound(x_j).get_rational(); k.addmul(new_a_ij, upper_bound(x_j).get_rational()); - upper(x_j)->push_justification(ante, numeral::zero(), proofs_enabled()); + upper(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } pol.push_back(row_entry(new_a_ij, x_j)); } @@ -566,7 +574,7 @@ namespace smt { } // k += new_a_ij * lower_bound(x_j).get_rational(); k.addmul(new_a_ij, lower_bound(x_j).get_rational()); - lower(x_j)->push_justification(ante, numeral::zero(), proofs_enabled()); + lower(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } else { SASSERT(at_upper(x_j)); @@ -579,7 +587,7 @@ namespace smt { new_a_ij.neg(); // the upper terms are inverted // k += new_a_ij * upper_bound(x_j).get_rational(); k.addmul(new_a_ij, upper_bound(x_j).get_rational()); - upper(x_j)->push_justification(ante, numeral::zero(), proofs_enabled()); + upper(x_j)->push_justification(ante, new_a_ij, coeffs_enabled()); } TRACE("gomory_cut_detail", tout << "new_a_ij: " << new_a_ij << "\n";); pol.push_back(row_entry(new_a_ij, x_j)); @@ -595,7 +603,7 @@ namespace smt { if (pol.empty()) { SASSERT(k.is_pos()); // conflict 0 >= k where k is positive - set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, true, "gomory_cut"); + set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, true, "gomory-cut"); return true; } else if (pol.size() == 1) { @@ -647,7 +655,7 @@ namespace smt { gomory_cut_justification( get_id(), ctx.get_region(), ante.lits().size(), ante.lits().c_ptr(), - ante.eqs().size(), ante.eqs().c_ptr(), l))); + ante.eqs().size(), ante.eqs().c_ptr(), ante, l))); return true; } @@ -772,8 +780,8 @@ namespace smt { // u += ncoeff * lower_bound(v).get_rational(); u.addmul(ncoeff, lower_bound(v).get_rational()); } - lower(v)->push_justification(ante, numeral::zero(), proofs_enabled()); - upper(v)->push_justification(ante, numeral::zero(), proofs_enabled()); + lower(v)->push_justification(ante, it->m_coeff, coeffs_enabled()); + upper(v)->push_justification(ante, it->m_coeff, coeffs_enabled()); } else if (gcds.is_zero()) { gcds = abs_ncoeff; @@ -1036,8 +1044,8 @@ namespace smt { num_args = 1; args = &n; } - for (unsigned j = 0; j < num_args; j++) { - expr * arg = args[j]; + for (unsigned i = 0; i < num_args; i++) { + expr * arg = args[i]; expr * pp; rational a_val; get_monomial(arg, a_val, pp); @@ -1056,6 +1064,7 @@ namespace smt { } if (!failed) { m_solver.assert_eq(as.size(), as.c_ptr(), xs.c_ptr(), c, j); + TRACE("euclidean_solver", tout << "add definition: v" << v << " := " << mk_ismt2_pp(n, t.get_manager()) << "\n";); } else { TRACE("euclidean_solver", tout << "failed for:\n" << mk_ismt2_pp(n, t.get_manager()) << "\n";); @@ -1186,7 +1195,8 @@ namespace smt { if (l != 0) { rational l_old = l->get_value().get_rational().to_rational(); rational l_new = g*ceil((l_old - c2)/g) + c2; - TRACE("euclidean_solver_new", tout << "new lower: " << l_new << " old: " << l_old << "\n";); + TRACE("euclidean_solver_new", tout << "new lower: " << l_new << " old: " << l_old << "\n"; + tout << "c: " << c2 << " ceil((l_old - c2)/g): " << (ceil((l_old - c2)/g)) << "\n";); if (l_new > l_old) { propagated = true; mk_lower(v, l_new, l, m_js); @@ -1379,6 +1389,7 @@ namespace smt { m_branch_cut_counter++; // TODO: add giveup code if (m_branch_cut_counter % m_params.m_arith_branch_cut_ratio == 0) { + TRACE("opt", display(tout);); move_non_base_vars_to_bounds(); if (!make_feasible()) { TRACE("arith_int", tout << "failed to move variables to bounds.\n";); @@ -1390,7 +1401,9 @@ namespace smt { TRACE("arith_int", tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";); SASSERT(is_base(int_var)); row const & r = m_rows[get_var_row(int_var)]; - mk_gomory_cut(r); + if (!mk_gomory_cut(r)) { + // silent failure + } return FC_CONTINUE; } } @@ -1400,7 +1413,7 @@ namespace smt { } theory_var int_var = find_infeasible_int_base_var(); if (int_var != null_theory_var) { - TRACE("arith_int", tout << "v" << int_var << " does not have and integer assignment: " << get_value(int_var) << "\n";); + TRACE("arith_int", tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";); // apply branching branch_infeasible_int_var(int_var); return FC_CONTINUE; diff --git a/src/smt/theory_arith_inv.h b/src/smt/theory_arith_inv.h index 80bb9307f..9c56ea367 100644 --- a/src/smt/theory_arith_inv.h +++ b/src/smt/theory_arith_inv.h @@ -199,6 +199,30 @@ namespace smt { } return true; } + + template + bool theory_arith::satisfy_integrality() const { + int num = get_num_vars(); + for (theory_var v = 0; v < num; v++) { + if (is_int(v) && !get_value(v).is_int()) { + TRACE("bound_bug", display_var(tout, v); display(tout);); + return false; + } + } + return true; + } + + template + bool theory_arith::valid_assignment() const { + if (valid_row_assignment() && + satisfy_bounds() && + satisfy_integrality()) { + return true; + } + TRACE("arith", display(tout);); + return false; + } + #endif }; diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index 6a7017a29..c2322fce2 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -333,12 +333,15 @@ namespace smt { void theory_arith::mul_bound_of(expr * var, unsigned power, interval & target) { theory_var v = expr2var(var); interval i = mk_interval_for(v); - TRACE("non_linear", tout << "bound: " << i << "\n" << mk_pp(var, get_manager()) << "\n"; + + TRACE("non_linear", + display_interval(tout << "bound: ",i); tout << i << "\n"; + tout << mk_pp(var, get_manager()) << "\n"; tout << "power " << power << ": " << expt(i, power) << "\n"; - tout << "target before: " << target << "\n";); + display_interval(tout << "target before: ", target); tout << "\n";); i.expt(power); target *= i; - TRACE("non_linear", tout << "target after: " << target << "\n";); + TRACE("non_linear", display_interval(tout << "target after: ", target); tout << "\n";); } /** @@ -427,12 +430,12 @@ namespace smt { template void theory_arith::mk_derived_nl_bound(theory_var v, inf_numeral const & coeff, bound_kind k, v_dependency * dep) { inf_numeral coeff_norm = normalize_bound(v, coeff, k); - TRACE("buggy_bound", tout << "v" << v << " " << coeff << " " << coeff_norm << " " << k << "\n";); derived_bound * new_bound = alloc(derived_bound, v, coeff_norm, k); m_bounds_to_delete.push_back(new_bound); m_asserted_bounds.push_back(new_bound); // copy justification to new bound dependency2new_bound(dep, *new_bound); + TRACE("buggy_bound", new_bound->display(*this, tout); tout << "\n";); } /** @@ -449,7 +452,8 @@ namespace smt { new_lower += get_epsilon(v); bound * old_lower = lower(v); if (old_lower == 0 || new_lower > old_lower->get_value()) { - TRACE("non_linear", tout << "NEW lower bound for v" << v << " " << new_lower << "\n";); + TRACE("non_linear", tout << "NEW lower bound for v" << v << " " << new_lower << "\n"; + display_interval(tout, i); tout << "\n";); mk_derived_nl_bound(v, new_lower, B_LOWER, i.get_lower_dependencies()); r = true; } @@ -460,7 +464,8 @@ namespace smt { new_upper -= get_epsilon(v); bound * old_upper = upper(v); if (old_upper == 0 || new_upper < old_upper->get_value()) { - TRACE("non_linear", tout << "NEW upper bound for v" << v << " " << new_upper << "\n";); + TRACE("non_linear", tout << "NEW upper bound for v" << v << " " << new_upper << "\n"; + display_interval(tout, i); tout << "\n";); mk_derived_nl_bound(v, new_upper, B_UPPER, i.get_upper_dependencies()); r = true; } @@ -897,31 +902,54 @@ namespace smt { m_tmp_lit_set.reset(); m_tmp_eq_set.reset(); - bool found_zero = false; SASSERT(is_pure_monomial(m)); - for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { - expr * arg = to_app(m)->get_arg(i); - if (!found_zero) { - theory_var _var = expr2var(arg); - if (is_fixed(_var)) { - bound * l = lower(_var); - bound * u = upper(_var); - if (l->get_value().is_zero()) { - /* if zero was found, then it is the explanation */ - SASSERT(k.is_zero()); - found_zero = true; - m_tmp_lit_set.reset(); - m_tmp_eq_set.reset(); - new_lower->m_lits.reset(); - new_lower->m_eqs.reset(); - } - accumulate_justification(*l, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); - accumulate_justification(*u, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); - } - } - } + bool found_zero = false; + for (unsigned i = 0; !found_zero && i < to_app(m)->get_num_args(); i++) { + expr * arg = to_app(m)->get_arg(i); + theory_var _var = expr2var(arg); + if (is_fixed(_var)) { + bound * l = lower(_var); + bound * u = upper(_var); + if (l->get_value().is_zero()) { + /* if zero was found, then it is the explanation */ + SASSERT(k.is_zero()); + found_zero = true; + m_tmp_lit_set.reset(); + m_tmp_eq_set.reset(); + new_lower->m_lits.reset(); + new_lower->m_eqs.reset(); + } + accumulate_justification(*l, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); + + TRACE("non_linear", + for (unsigned j = 0; j < new_lower->m_lits.size(); ++j) { + ctx.display_detailed_literal(tout, new_lower->m_lits[j]); + tout << " "; + } + tout << "\n";); + + accumulate_justification(*u, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); + + TRACE("non_linear", + for (unsigned j = 0; j < new_lower->m_lits.size(); ++j) { + ctx.display_detailed_literal(tout, new_lower->m_lits[j]); + tout << " "; + } + tout << "\n";); + + } + } new_upper->m_lits.append(new_lower->m_lits); new_upper->m_eqs.append(new_lower->m_eqs); + + TRACE("non_linear", + tout << "lower: " << new_lower << " upper: " << new_upper << "\n"; + for (unsigned j = 0; j < new_upper->m_lits.size(); ++j) { + ctx.display_detailed_literal(tout, new_upper->m_lits[j]); + tout << " "; + } + tout << "\n";); + return true; } @@ -1887,6 +1915,10 @@ namespace smt { derived_bound b(null_theory_var, inf_numeral(0), B_LOWER); dependency2new_bound(d, b); set_conflict(b.m_lits.size(), b.m_lits.c_ptr(), b.m_eqs.size(), b.m_eqs.c_ptr(), ante, is_lia, "arith_nl"); + TRACE("non_linear", + for (unsigned i = 0; i < b.m_lits.size(); ++i) { + tout << b.m_lits[i] << " "; + }); } /** @@ -2339,7 +2371,7 @@ namespace smt { return FC_CONTINUE; } - if (!max_min_nl_vars()) + if (!max_min_nl_vars()) return FC_CONTINUE; if (check_monomial_assignments()) { diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index 274cd5499..f954f082c 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -395,9 +395,30 @@ namespace smt { template void theory_arith::display_bound(std::ostream & out, bound * b, unsigned indent) const { for (unsigned i = 0; i < indent; i++) out << " "; - theory_var v = b->get_var(); - enode * e = get_enode(v); - out << "v" << v << " #" << e->get_owner_id() << " " << (b->get_bound_kind() == B_LOWER ? ">=" : "<=") << " " << b->get_value() << "\n"; + b->display(*this, out); + out << "\n"; + } + + template + void theory_arith::display_deps(std::ostream & out, v_dependency* dep) { + ptr_vector bounds; + m_dep_manager.linearize(dep, bounds); + m_tmp_lit_set.reset(); + m_tmp_eq_set.reset(); + ptr_vector::const_iterator it = bounds.begin(); + ptr_vector::const_iterator end = bounds.end(); + for (; it != end; ++it) { + bound * b = static_cast(*it); + out << " "; + b->display(*this, out); + } + } + + template + void theory_arith::display_interval(std::ostream & out, interval const& i) { + i.display(out); + display_deps(out << " lo:", i.get_lower_dependencies()); + display_deps(out << " hi:", i.get_upper_dependencies()); } template @@ -428,7 +449,7 @@ namespace smt { template void theory_arith::display_atom(std::ostream & out, atom * a, bool show_sign) const { theory_var v = a->get_var(); - numeral const & k = a->get_k(); + inf_numeral const & k = a->get_k(); enode * e = get_enode(v); if (show_sign) { if (!a->is_true()) diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 28314c421..4e169a8be 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -208,6 +208,8 @@ namespace smt { theory_array::reset_eh(); std::for_each(m_var_data_full.begin(), m_var_data_full.end(), delete_proc()); m_var_data_full.reset(); + m_eqs.reset(); + m_eqsv.reset(); } void theory_array_full::display_var(std::ostream & out, theory_var v) const { @@ -223,7 +225,6 @@ namespace smt { } theory_var theory_array_full::mk_var(enode * n) { - theory_var r = theory_array::mk_var(n); SASSERT(r == static_cast(m_var_data_full.size())); m_var_data_full.push_back(alloc(var_data_full)); @@ -512,7 +513,7 @@ namespace smt { TRACE("array_map_bug", tout << "select-map axiom\n" << mk_ismt2_pp(sel1, m) << "\n=\n" << mk_ismt2_pp(sel2,m) << "\n";); - + return try_assign_eq(sel1, sel2); } @@ -760,37 +761,36 @@ namespace smt { r = FC_CONTINUE; } } + while (!m_eqsv.empty()) { + literal eq = m_eqsv.back(); + m_eqsv.pop_back(); + get_context().mark_as_relevant(eq); + assert_axiom(eq); + r = FC_CONTINUE; + } if (r == FC_DONE && m_found_unsupported_op) r = FC_GIVEUP; return r; } + bool theory_array_full::try_assign_eq(expr* v1, expr* v2) { - context& ctx = get_context(); - enode* n1 = ctx.get_enode(v1); - enode* n2 = ctx.get_enode(v2); - if (n1->get_root() == n2->get_root()) { - return false; - } TRACE("array", tout << mk_bounded_pp(v1, get_manager()) << "\n==\n" << mk_bounded_pp(v2, get_manager()) << "\n";); - -#if 0 - if (m.proofs_enabled()) { -#endif - literal eq(mk_eq(v1,v2,true)); - ctx.mark_as_relevant(eq); - assert_axiom(eq); -#if 0 + context& ctx = get_context(); + if (m_eqs.contains(v1, v2)) { + return false; } else { - ctx.mark_as_relevant(n1); - ctx.mark_as_relevant(n2); - ctx.assign_eq(n1, n2, eq_justification::mk_axiom()); + m_eqs.insert(v1, v2, true); + literal eq(mk_eq(v1, v2, true)); + get_context().mark_as_relevant(eq); + assert_axiom(eq); + + // m_eqsv.push_back(eq); + return true; } -#endif - return true; } void theory_array_full::pop_scope_eh(unsigned num_scopes) { @@ -798,6 +798,8 @@ namespace smt { theory_array::pop_scope_eh(num_scopes); std::for_each(m_var_data_full.begin() + num_old_vars, m_var_data_full.end(), delete_proc()); m_var_data_full.shrink(num_old_vars); + m_eqs.reset(); + m_eqsv.reset(); } void theory_array_full::collect_statistics(::statistics & st) const { diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h index 5a80b7fae..bbbf35408 100644 --- a/src/smt/theory_array_full.h +++ b/src/smt/theory_array_full.h @@ -19,8 +19,9 @@ Revision History: #ifndef _THEORY_ARRAY_FULL_H_ #define _THEORY_ARRAY_FULL_H_ -#include"theory_array.h" +#include "theory_array.h" #include "simplifier.h" +#include "ast_trail.h" namespace smt { @@ -37,12 +38,12 @@ namespace smt { ast2ast_trailmap m_sort2epsilon; simplifier* m_simp; + obj_pair_map m_eqs; + svector m_eqsv; protected: -#if 0 - virtual final_check_status final_check_eh(); -#endif + //virtual final_check_status final_check_eh(); virtual void reset_eh(); virtual void set_prop_upward(theory_var v); @@ -83,6 +84,7 @@ namespace smt { bool try_assign_eq(expr* n1, expr* n2); + void assign_eqs(); public: diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index 8c6543eff..a004da666 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -61,6 +61,13 @@ namespace smt { if (antecedent == null_literal) { ctx.assign_eq(lhs, ctx.get_enode(rhs), eq_justification::mk_axiom()); } + else if (ctx.get_assignment(antecedent) != l_true) { + literal l(mk_eq(lhs->get_owner(), rhs, true)); + ctx.mark_as_relevant(l); + ctx.mark_as_relevant(antecedent); + literal lits[2] = {l, ~antecedent}; + ctx.mk_th_axiom(get_id(), 2, lits); + } else { SASSERT(ctx.get_assignment(antecedent) == l_true); region & r = ctx.get_region(); @@ -143,6 +150,47 @@ namespace smt { ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, 1, &l, 1, &p))); } + /** + \brief Given a field update n := { r with field := v } for constructor C, assert the axioms: + (=> (is-C r) (= (acc_j n) (acc_j r))) for acc_j != field + (=> (is-C r) (= (field n) v)) for acc_j != field + (=> (not (is-C r)) (= n r)) + */ + void theory_datatype::assert_update_field_axioms(enode * n) { + m_stats.m_assert_update_field++; + SASSERT(is_update_field(n)); + context & ctx = get_context(); + ast_manager & m = get_manager(); + app* own = n->get_owner(); + expr* arg1 = own->get_arg(0); + func_decl * upd = n->get_decl(); + func_decl * acc = to_func_decl(upd->get_parameter(0).get_ast()); + func_decl * con = m_util.get_accessor_constructor(acc); + func_decl * rec = m_util.get_constructor_recognizer(con); + ptr_vector const * accessors = m_util.get_constructor_accessors(con); + ptr_vector::const_iterator it = accessors->begin(); + ptr_vector::const_iterator end = accessors->end(); + app_ref rec_app(m.mk_app(rec, arg1), m); + ctx.internalize(rec_app, false); + literal is_con(ctx.get_bool_var(rec_app)); + for (; it != end; ++it) { + enode* arg; + func_decl * acc1 = *it; + if (acc1 == acc) { + arg = n->get_arg(1); + } + else { + app* acc_app = m.mk_app(acc1, arg1); + ctx.internalize(acc_app, false); + arg = ctx.get_enode(acc_app); + } + app * acc_own = m.mk_app(acc1, own); + assert_eq_axiom(arg, acc_own, is_con); + } + // update_field is identity if 'n' is not created by a matching constructor. + assert_eq_axiom(n, arg1, ~is_con); + } + theory_var theory_datatype::mk_var(enode * n) { theory_var r = theory::mk_var(n); theory_var r2 = m_find.mk_var(); @@ -150,15 +198,17 @@ namespace smt { SASSERT(r == static_cast(m_var_data.size())); m_var_data.push_back(alloc(var_data)); var_data * d = m_var_data[r]; + context & ctx = get_context(); + ctx.attach_th_var(n, this, r); if (is_constructor(n)) { d->m_constructor = n; - get_context().attach_th_var(n, this, r); assert_accessor_axioms(n); } + else if (is_update_field(n)) { + assert_update_field_axioms(n); + } else { ast_manager & m = get_manager(); - context & ctx = get_context(); - ctx.attach_th_var(n, this, r); sort * s = m.get_sort(n->get_owner()); if (m_util.get_datatype_num_constructors(s) == 1) { func_decl * c = m_util.get_datatype_constructors(s)->get(0); @@ -192,7 +242,7 @@ namespace smt { ctx.set_var_theory(bv, get_id()); ctx.set_enode_flag(bv, true); } - if (is_constructor(term)) { + if (is_constructor(term) || is_update_field(term)) { SASSERT(!is_attached_to_var(e)); // *** We must create a theory variable for each argument that has sort datatype *** // @@ -478,6 +528,7 @@ namespace smt { st.update("datatype splits", m_stats.m_splits); st.update("datatype constructor ax", m_stats.m_assert_cnstr); st.update("datatype accessor ax", m_stats.m_assert_accessor); + st.update("datatype update ax", m_stats.m_assert_update_field); } void theory_datatype::display_var(std::ostream & out, theory_var v) const { diff --git a/src/smt/theory_datatype.h b/src/smt/theory_datatype.h index cf2f933ad..5ebfc220f 100644 --- a/src/smt/theory_datatype.h +++ b/src/smt/theory_datatype.h @@ -41,7 +41,7 @@ namespace smt { struct stats { unsigned m_occurs_check, m_splits; - unsigned m_assert_cnstr, m_assert_accessor; + unsigned m_assert_cnstr, m_assert_accessor, m_assert_update_field; void reset() { memset(this, 0, sizeof(stats)); } stats() { reset(); } }; @@ -58,14 +58,17 @@ namespace smt { bool is_constructor(app * f) const { return m_util.is_constructor(f); } bool is_recognizer(app * f) const { return m_util.is_recognizer(f); } bool is_accessor(app * f) const { return m_util.is_accessor(f); } - + bool is_update_field(app * f) const { return m_util.is_update_field(f); } + bool is_constructor(enode * n) const { return is_constructor(n->get_owner()); } bool is_recognizer(enode * n) const { return is_recognizer(n->get_owner()); } bool is_accessor(enode * n) const { return is_accessor(n->get_owner()); } + bool is_update_field(enode * n) const { return m_util.is_update_field(n->get_owner()); } void assert_eq_axiom(enode * lhs, expr * rhs, literal antecedent); void assert_is_constructor_axiom(enode * n, func_decl * c, literal antecedent); void assert_accessor_axioms(enode * n); + void assert_update_field_axioms(enode * n); void add_recognizer(theory_var v, enode * recognizer); void propagate_recognizer(theory_var v, enode * r); void sign_recognizer_conflict(enode * c, enode * r); diff --git a/src/smt/theory_dense_diff_logic.h b/src/smt/theory_dense_diff_logic.h index 355e38c0c..04e257889 100644 --- a/src/smt/theory_dense_diff_logic.h +++ b/src/smt/theory_dense_diff_logic.h @@ -25,6 +25,8 @@ TODO: eager equality propagation #include"theory_arith_params.h" #include"arith_decl_plugin.h" #include"arith_eq_adapter.h" +#include"theory_opt.h" + namespace smt { @@ -41,7 +43,7 @@ namespace smt { }; template - class theory_dense_diff_logic : public theory, private Ext { + class theory_dense_diff_logic : public theory, public theory_opt, private Ext { public: theory_dense_diff_logic_statistics m_stats; @@ -127,6 +129,12 @@ namespace smt { svector m_scopes; bool m_non_diff_logic_exprs; + // For optimization purpose + typedef vector > objective_term; + vector m_objectives; + vector m_objective_consts; + vector m_objective_assignments; + struct f_target { theory_var m_target; numeral m_new_distance; @@ -189,6 +197,8 @@ namespace smt { void del_atoms(unsigned old_size); void del_vars(unsigned old_num_vars); void init_model(); + bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); + expr_ref mk_ineq(theory_var v, inf_rational const& val, bool is_strict); #ifdef Z3DEBUG bool check_vector_sizes() const; bool check_matrix() const; @@ -229,6 +239,8 @@ namespace smt { virtual void flush_eh(); virtual void reset_eh(); + bool dump_lemmas() const { return m_params.m_arith_dump_lemmas; } + virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const; virtual void display(std::ostream & out) const; @@ -249,6 +261,18 @@ namespace smt { virtual void init_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & mg); + // ----------------------------------- + // + // Optimization + // + // ----------------------------------- + + virtual inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared); + virtual inf_eps_rational value(theory_var v); + virtual theory_var add_objective(app* term); + virtual expr_ref mk_gt(theory_var v, inf_rational const& val); + virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val); + // ----------------------------------- // // Main diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 21f1262ac..89e23cd3f 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -23,6 +23,8 @@ Revision History: #include"theory_dense_diff_logic.h" #include"ast_pp.h" #include"smt_model_generator.h" +#include"simplex.h" +#include"simplex_def.h" namespace smt { @@ -120,6 +122,7 @@ namespace smt { if (!m_non_diff_logic_exprs) { TRACE("non_diff_logic", tout << "found non diff logic expression:\n" << mk_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_non_diff_logic_exprs)); + IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, get_manager()) << ")\n";); m_non_diff_logic_exprs = true; } } @@ -587,6 +590,11 @@ namespace smt { context & ctx = get_context(); region & r = ctx.get_region(); ctx.set_conflict(ctx.mk_justification(theory_conflict_justification(get_id(), r, antecedents.size(), antecedents.c_ptr()))); + + if (dump_lemmas()) { + ctx.display_lemma_as_smt_problem(antecedents.size(), antecedents.c_ptr(), false_literal, ""); + } + return; } @@ -818,6 +826,273 @@ namespace smt { return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int(v))); } + // TBD: code is common to both sparse and dense difference logic solvers. + template + bool theory_dense_diff_logic::internalize_objective(expr * n, rational const& m, rational& q, objective_term & objective) { + + // Compile term into objective_term format + rational r; + expr* x, *y; + if (m_autil.is_numeral(n, r)) { + q += r; + } + else if (m_autil.is_add(n)) { + for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { + if (!internalize_objective(to_app(n)->get_arg(i), m, q, objective)) { + return false; + } + } + } + else if (m_autil.is_mul(n, x, y) && m_autil.is_numeral(x, r)) { + return internalize_objective(y, m*r, q, objective); + } + else if (m_autil.is_mul(n, y, x) && m_autil.is_numeral(x, r)) { + return internalize_objective(y, m*r, q, objective); + } + else if (!is_app(n)) { + return false; + } + else if (to_app(n)->get_family_id() == m_autil.get_family_id()) { + return false; + } + else { + context& ctx = get_context(); + enode * e = 0; + theory_var v = 0; + if (ctx.e_internalized(n)) { + e = ctx.get_enode(to_app(n)); + } + else { + e = ctx.mk_enode(to_app(n), false, false, true); + } + v = e->get_th_var(get_id()); + if (v == null_theory_var) { + v = mk_var(e); + } + + objective.push_back(std::make_pair(v, m)); + } + return true; + } + + template + inf_eps_rational theory_dense_diff_logic::value(theory_var v) { + objective_term const& objective = m_objectives[v]; + inf_eps r = inf_eps(m_objective_consts[v]); + for (unsigned i = 0; i < objective.size(); ++i) { + numeral n = m_assignment[v]; + rational r1 = n.get_rational().to_rational(); + rational r2 = n.get_infinitesimal().to_rational(); + r += objective[i].second * inf_eps(rational(0), inf_rational(r1, r2)); + } + return r; + } + + template + inf_eps_rational theory_dense_diff_logic::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + typedef simplex::simplex Simplex; + Simplex S; + ast_manager& m = get_manager(); + objective_term const& objective = m_objectives[v]; + has_shared = false; + + IF_VERBOSE(1, + for (unsigned i = 0; i < objective.size(); ++i) { + verbose_stream() << objective[i].second + << " * v" << objective[i].first << " "; + } + verbose_stream() << " + " << m_objective_consts[v] << "\n";); + + unsigned num_nodes = get_num_vars(); + unsigned num_edges = m_edges.size(); + S.ensure_var(num_nodes + num_edges + m_objectives.size()); + for (unsigned i = 0; i < num_nodes; ++i) { + numeral const& a = m_assignment[i]; + rational fin = a.get_rational().to_rational(); + rational inf = a.get_infinitesimal().to_rational(); + mpq_inf q(fin.to_mpq(), inf.to_mpq()); + S.set_value(i, q); + } + for (unsigned i = 0; i < num_nodes; ++i) { + enode * n = get_enode(i); + if (m_autil.is_zero(n->get_owner())) { + S.set_lower(v, mpq_inf(mpq(0), mpq(0))); + S.set_upper(v, mpq_inf(mpq(0), mpq(0))); + break; + } + } + svector vars; + unsynch_mpq_manager mgr; + scoped_mpq_vector coeffs(mgr); + coeffs.push_back(mpq(1)); + coeffs.push_back(mpq(-1)); + coeffs.push_back(mpq(-1)); + vars.resize(3); + for (unsigned i = 0; i < num_edges; ++i) { + edge const& e = m_edges[i]; + if (e.m_source == null_theory_var || e.m_target == null_theory_var) { + continue; + } + unsigned base_var = num_nodes + i; + vars[0] = e.m_target; + vars[1] = e.m_source; + vars[2] = base_var; + S.add_row(base_var, 3, vars.c_ptr(), coeffs.c_ptr()); + // t - s <= w + // t - s - b = 0, b >= w + numeral const& w = e.m_offset; + rational fin = w.get_rational().to_rational(); + rational inf = w.get_infinitesimal().to_rational(); + mpq_inf q(fin.to_mpq(),inf.to_mpq()); + S.set_upper(base_var, q); + } + unsigned w = num_nodes + num_edges + v; + + // add objective function as row. + coeffs.reset(); + vars.reset(); + for (unsigned i = 0; i < objective.size(); ++i) { + coeffs.push_back(objective[i].second.to_mpq()); + vars.push_back(objective[i].first); + } + coeffs.push_back(mpq(1)); + vars.push_back(w); + Simplex::row row = S.add_row(w, vars.size(), vars.c_ptr(), coeffs.c_ptr()); + + TRACE("opt", S.display(tout); display(tout);); + + // optimize + lbool is_sat = S.make_feasible(); + if (is_sat == l_undef) { + blocker = m.mk_false(); + return inf_eps::infinity(); + } + TRACE("opt", S.display(tout); ); + SASSERT(is_sat != l_false); + lbool is_fin = S.minimize(w); + + switch (is_fin) { + case l_true: { + simplex::mpq_ext::eps_numeral const& val = S.get_value(w); + inf_rational r(-rational(val.first), -rational(val.second)); + TRACE("opt", tout << r << " " << "\n"; + S.display_row(tout, row, true);); + Simplex::row_iterator it = S.row_begin(row), end = S.row_end(row); + expr_ref_vector& core = m_objective_assignments[v]; + expr_ref tmp(m); + core.reset(); + for (; it != end; ++it) { + unsigned v = it->m_var; + if (num_nodes <= v && v < num_nodes + num_edges) { + unsigned edge_id = v - num_nodes; + literal lit = m_edges[edge_id].m_justification; + get_context().literal2expr(lit, tmp); + core.push_back(tmp); + } + } + for (unsigned i = 0; i < num_nodes; ++i) { + mpq_inf const& val = S.get_value(i); + rational q(val.first), eps(val.second); + numeral a(q); + m_assignment[i] = a; + // TBD: if epsilon is != 0, then adjust a by some small fraction. + } + blocker = mk_gt(v, r); + IF_VERBOSE(10, verbose_stream() << blocker << "\n";); + return inf_eps(rational(0), r); + } + default: + TRACE("opt", tout << "unbounded\n"; ); + blocker = m.mk_false(); + return inf_eps::infinity(); + } + } + + template + theory_var theory_dense_diff_logic::add_objective(app* term) { + objective_term objective; + theory_var result = m_objectives.size(); + rational q(1), r(0); + expr_ref_vector vr(get_manager()); + if (!is_linear(get_manager(), term)) { + result = null_theory_var; + } + else if (internalize_objective(term, q, r, objective)) { + m_objectives.push_back(objective); + m_objective_consts.push_back(r); + m_objective_assignments.push_back(vr); + } + else { + result = null_theory_var; + } + return result; + } + + template + expr_ref theory_dense_diff_logic::mk_gt(theory_var v, inf_rational const& val) { + return mk_ineq(v, val, true); + } + + template + expr_ref theory_dense_diff_logic::mk_ge( + filter_model_converter& fm, theory_var v, inf_rational const& val) { + return mk_ineq(v, val, false); + } + + template + expr_ref theory_dense_diff_logic::mk_ineq(theory_var v, inf_rational const& val, bool is_strict) { + ast_manager& m = get_manager(); + objective_term const& t = m_objectives[v]; + expr_ref e(m), f(m), f2(m); + if (t.size() == 1 && t[0].second.is_one()) { + f = get_enode(t[0].first)->get_owner(); + } + else if (t.size() == 1 && t[0].second.is_minus_one()) { + f = m_autil.mk_uminus(get_enode(t[0].first)->get_owner()); + } + else if (t.size() == 2 && t[0].second.is_one() && t[1].second.is_minus_one()) { + f = get_enode(t[0].first)->get_owner(); + f2 = get_enode(t[1].first)->get_owner(); + f = m_autil.mk_sub(f, f2); + } + else if (t.size() == 2 && t[1].second.is_one() && t[0].second.is_minus_one()) { + f = get_enode(t[1].first)->get_owner(); + f2 = get_enode(t[0].first)->get_owner(); + f = m_autil.mk_sub(f, f2); + } + else { + // + expr_ref_vector const& core = m_objective_assignments[v]; + f = m.mk_and(core.size(), core.c_ptr()); + if (is_strict) { + f = m.mk_not(f); + } + TRACE("arith", tout << "block: " << f << "\n";); + return f; + } + + e = m_autil.mk_numeral(val.get_rational(), m.get_sort(f)); + + if (val.get_infinitesimal().is_neg()) { + if (is_strict) { + f = m_autil.mk_ge(f, e); + } + else { + expr_ref_vector const& core = m_objective_assignments[v]; + f = m.mk_and(core.size(), core.c_ptr()); + } + } + else { + if (is_strict) { + f = m_autil.mk_gt(f, e); + } + else { + f = m_autil.mk_ge(f, e); + } + } + return f; + } + }; #endif /* _THEORY_DENSE_DIFF_LOGIC_DEF_H_ */ diff --git a/src/smt/theory_diff_logic.cpp b/src/smt/theory_diff_logic.cpp index a68b7782b..f1f00833b 100644 --- a/src/smt/theory_diff_logic.cpp +++ b/src/smt/theory_diff_logic.cpp @@ -21,6 +21,7 @@ Revision History: #include"rational.h" #include"theory_diff_logic_def.h" +#include"sparse_matrix_def.h" namespace smt { @@ -29,4 +30,10 @@ template class theory_diff_logic; template class theory_diff_logic; template class theory_diff_logic; + +}; + +namespace simplex { +template class simplex; +template class sparse_matrix; }; diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 7302ccfd4..8b5bb2b86 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -37,7 +37,9 @@ Revision History: #include"smt_model_generator.h" #include"numeral_factory.h" #include"smt_clause.h" - +#include"theory_opt.h" +#include"simplex.h" +#include"simplex_def.h" // The DL theory can represent term such as n + k, where n is an enode and k is a numeral. namespace smt { @@ -59,9 +61,11 @@ namespace smt { }; template - class theory_diff_logic : public theory, private Ext { + class theory_diff_logic : public theory, public theory_opt, private Ext { typedef typename Ext::numeral numeral; + typedef simplex::simplex Simplex; + typedef inf_eps_rational inf_eps; class atom { bool_var m_bvar; @@ -158,18 +162,21 @@ namespace smt { unsigned m_asserted_atoms_lim; unsigned m_asserted_qhead_old; }; + typedef dl_graph Graph; smt_params & m_params; arith_util m_util; arith_eq_adapter m_arith_eq_adapter; theory_diff_logic_statistics m_stats; - dl_graph m_graph; - theory_var m_zero_int; // cache the variable representing the zero variable. - theory_var m_zero_real; // cache the variable representing the zero variable. + Graph m_graph; + theory_var m_zero; // cache the variable representing the zero variable. int_vector m_scc_id; // Cheap equality propagation eq_prop_info_set m_eq_prop_info_set; // set of existing equality prop infos ptr_vector m_eq_prop_infos; + app_ref_vector m_terms; + svector m_signs; + ptr_vector m_atoms; ptr_vector m_asserted_atoms; // set of asserted atoms unsigned m_asserted_qhead; @@ -184,7 +191,16 @@ namespace smt { arith_factory * m_factory; rational m_delta; - nc_functor m_nc_functor; + nc_functor m_nc_functor; + + // For optimization purpose + typedef vector > objective_term; + vector m_objectives; + vector m_objective_consts; + vector m_objective_assignments; + vector m_objective_rows; + Simplex m_S; + unsigned m_num_simplex_edges; // Set a conflict due to a negative cycle. void set_neg_cycle_conflict(); @@ -210,8 +226,8 @@ namespace smt { m_params(params), m_util(m), m_arith_eq_adapter(*this, params, m_util), - m_zero_int(null_theory_var), - m_zero_real(null_theory_var), + m_zero(null_theory_var), + m_terms(m), m_asserted_qhead(0), m_num_core_conflicts(0), m_num_propagation_calls(0), @@ -219,7 +235,8 @@ namespace smt { m_is_lia(true), m_non_diff_logic_exprs(false), m_factory(0), - m_nc_functor(*this) { + m_nc_functor(*this), + m_num_simplex_edges(0) { } virtual ~theory_diff_logic() { @@ -296,12 +313,33 @@ namespace smt { virtual void collect_statistics(::statistics & st) const; - private: + + // ----------------------------------- + // + // Optimization + // + // ----------------------------------- + + virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared); + virtual inf_eps value(theory_var v); + virtual theory_var add_objective(app* term); + virtual expr_ref mk_gt(theory_var v, inf_rational const& val); + virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val); + + bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); + + private: + + expr_ref mk_ineq(theory_var v, inf_rational const& val, bool is_strict); virtual void new_eq_eh(theory_var v1, theory_var v2, justification& j); virtual void new_diseq_eh(theory_var v1, theory_var v2, justification& j); + bool decompose_linear(app_ref_vector& args, svector& signs); + + bool is_sign(expr* n, bool& sign); + bool is_negative(app* n, app*& m); void del_atoms(unsigned old_size); @@ -336,18 +374,26 @@ namespace smt { void get_implied_bound_antecedents(edge_id bridge_edge, edge_id subsumed_edge, conflict_resolution & cr); - theory_var get_zero(sort* s) const { return m_util.is_int(s)?m_zero_int:m_zero_real; } - - theory_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } + theory_var get_zero() const { return m_zero; } void inc_conflicts(); + // Optimization: + // convert variables, edges and objectives to simplex. + unsigned node2simplex(unsigned v); + unsigned edge2simplex(unsigned e); + unsigned obj2simplex(unsigned v); + unsigned num_simplex_vars(); + bool is_simplex_edge(unsigned e); + unsigned simplex2edge(unsigned e); + void update_simplex(Simplex& S); }; struct idl_ext { // TODO: It doesn't need to be a rational, but a bignum integer. static const bool m_int_theory = true; typedef rational numeral; + typedef rational fin_numeral; numeral m_epsilon; idl_ext() : m_epsilon(1) {} }; @@ -356,6 +402,7 @@ namespace smt { // TODO: It doesn't need to be a rational, but a bignum integer. static const bool m_int_theory = true; typedef s_integer numeral; + typedef s_integer fin_numeral; numeral m_epsilon; sidl_ext() : m_epsilon(1) {} }; @@ -363,18 +410,19 @@ namespace smt { struct rdl_ext { static const bool m_int_theory = false; typedef inf_int_rational numeral; - numeral m_epsilon; + typedef rational fin_numeral; + numeral m_epsilon; rdl_ext() : m_epsilon(rational(), true) {} }; struct srdl_ext { static const bool m_int_theory = false; typedef inf_s_integer numeral; + typedef s_integer fin_numeral; numeral m_epsilon; srdl_ext() : m_epsilon(s_integer(0),true) {} }; - typedef theory_diff_logic theory_idl; typedef theory_diff_logic theory_fidl; typedef theory_diff_logic theory_rdl; diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index a2e1ece40..6c43c8d11 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -28,6 +28,8 @@ Revision History: #include"ast_pp.h" #include"warning.h" #include"smt_model_generator.h" +#include"model_implicant.h" + using namespace smt; @@ -69,12 +71,7 @@ void theory_diff_logic::init(context * ctx) { zero = m_util.mk_numeral(rational(0), true); e = ctx->mk_enode(zero, false, false, true); SASSERT(!is_attached_to_var(e)); - m_zero_int = mk_var(e); - - zero = m_util.mk_numeral(rational(0), false); - e = ctx->mk_enode(zero, false, false, true); - SASSERT(!is_attached_to_var(e)); - m_zero_real = mk_var(e); + m_zero = mk_var(e); } @@ -82,8 +79,10 @@ template bool theory_diff_logic::internalize_term(app * term) { bool result = null_theory_var != mk_term(term); CTRACE("arith", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); - TRACE("non_diff_logic", tout << "Terms may not be internalized\n";); - found_non_diff_logic_expr(term); + if (!result) { + TRACE("non_diff_logic", tout << "Terms may not be internalized\n";); + found_non_diff_logic_expr(term); + } return result; } @@ -159,6 +158,7 @@ template void theory_diff_logic::found_non_diff_logic_expr(expr * n) { if (!m_non_diff_logic_exprs) { TRACE("non_diff_logic", tout << "found non diff logic expression:\n" << mk_pp(n, get_manager()) << "\n";); + IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, get_manager()) << ")\n";); get_context().push_trail(value_trail(m_non_diff_logic_exprs)); m_non_diff_logic_exprs = true; } @@ -177,7 +177,6 @@ bool theory_diff_logic::internalize_atom(app * n, bool gate_ctx) { bool is_ge = m_util.is_ge(n); bool_var bv; rational kr; - app * x, *y, *z; theory_var source, target; // target - source <= k app * lhs = to_app(n->get_arg(0)); app * rhs = to_app(n->get_arg(1)); @@ -191,25 +190,26 @@ bool theory_diff_logic::internalize_atom(app * n, bool gate_ctx) { } numeral k(kr); - bool is_add = m_util.is_add(lhs) && lhs->get_num_args() == 2; - - if (is_add) { - x = to_app(lhs->get_arg(0)); - y = to_app(lhs->get_arg(1)); + m_terms.reset(); + m_signs.reset(); + m_terms.push_back(lhs); + m_signs.push_back(true); + if (!decompose_linear(m_terms, m_signs)) { + found_non_diff_logic_expr(n); + return false; } - - if (is_add && is_negative(x, z)) { - target = mk_var(y); - source = mk_var(z); - } - else if (is_add && is_negative(y, z)) { - target = mk_var(x); - source = mk_var(z); + if (m_terms.size() == 2 && m_signs[0] != m_signs[1]) { + target = mk_var(m_terms[0].get()); + source = mk_var(m_terms[1].get()); + if (!m_signs[0]) { + std::swap(target, source); + } } else { target = mk_var(lhs); - source = get_zero(lhs); + source = get_zero(); } + if (is_ge) { std::swap(target, source); k.neg(); @@ -273,6 +273,8 @@ bool theory_diff_logic::internalize_atom(app * n, bool gate_ctx) { template void theory_diff_logic::internalize_eq_eh(app * atom, bool_var v) { context & ctx = get_context(); + ast_manager& m = get_manager(); + TRACE("arith", tout << mk_pp(atom, m) << "\n";); app * lhs = to_app(atom->get_arg(0)); app * rhs = to_app(atom->get_arg(1)); app * s; @@ -339,7 +341,13 @@ void theory_diff_logic::pop_scope_eh(unsigned num_scopes) { m_asserted_atoms.shrink(s.m_asserted_atoms_lim); m_asserted_qhead = s.m_asserted_qhead_old; m_scopes.shrink(new_lvl); + unsigned num_edges = m_graph.get_num_edges(); m_graph.pop(num_scopes); + if (num_edges != m_graph.get_num_edges() && m_num_simplex_edges > 0) { + m_S.reset(); + m_num_simplex_edges = 0; + m_objective_rows.reset(); + } theory::pop_scope_eh(num_scopes); } @@ -353,7 +361,7 @@ final_check_status theory_diff_logic::final_check_eh() { TRACE("arith_final", display(tout); ); // either will already be zero (as we don't do mixed constraints). - m_graph.set_to_zero(m_zero_int, m_zero_real); + m_graph.set_to_zero(m_zero); SASSERT(is_consistent()); if (m_non_diff_logic_exprs) { return FC_GIVEUP; @@ -378,6 +386,70 @@ void theory_diff_logic::del_atoms(unsigned old_size) { } +template +bool theory_diff_logic::decompose_linear(app_ref_vector& terms, svector& signs) { + for (unsigned i = 0; i < terms.size(); ++i) { + app* n = terms[i].get(); + if (m_util.is_add(n)) { + expr* arg = n->get_arg(0); + if (!is_app(arg)) return false; + terms[i] = to_app(arg); + for (unsigned j = 1; j < n->get_num_args(); ++j) { + arg = n->get_arg(j); + if (!is_app(arg)) return false; + terms.push_back(to_app(arg)); + signs.push_back(signs[i]); + } + --i; + continue; + } + expr* x, *y; + bool sign; + if (m_util.is_mul(n, x, y)) { + if (is_sign(x, sign) && is_app(y)) { + terms[i] = to_app(y); + signs[i] = (signs[i] == sign); + --i; + } + else if (is_sign(y, sign) && is_app(x)) { + terms[i] = to_app(x); + signs[i] = (signs[i] == sign); + --i; + } + continue; + } + if (m_util.is_uminus(n, x) && is_app(x)) { + terms[i] = to_app(x); + signs[i] = !signs[i]; + --i; + continue; + } + } + return true; +} + +template +bool theory_diff_logic::is_sign(expr* n, bool& sign) { + rational r; + expr* x; + if (m_util.is_numeral(n, r)) { + if (r.is_one()) { + sign = true; + return true; + } + if (r.is_minus_one()) { + sign = false; + return true; + } + } + else if (m_util.is_uminus(n, x)) { + if (is_sign(x, sign)) { + sign = !sign; + return true; + } + } + return false; +} template bool theory_diff_logic::is_negative(app* n, app*& m) { @@ -474,7 +546,7 @@ void theory_diff_logic::propagate_core() { template bool theory_diff_logic::propagate_atom(atom* a) { context& ctx = get_context(); - TRACE("arith", a->display(*this, tout); ); + TRACE("arith", a->display(*this, tout); tout << "\n";); if (ctx.inconsistent()) { return false; } @@ -650,6 +722,7 @@ theory_var theory_diff_logic::mk_term(app* n) { app* a, *offset; theory_var source, target; enode* e; + context& ctx = get_context(); TRACE("arith", tout << mk_pp(n, get_manager()) << "\n";); @@ -660,6 +733,13 @@ theory_var theory_diff_logic::mk_term(app* n) { else if (is_offset(n, a, offset, r)) { // n = a + k source = mk_var(a); + for (unsigned i = 0; i < n->get_num_args(); ++i) { + expr* arg = n->get_arg(i); + std::cout << "internalize: " << mk_pp(arg, get_manager()) << " " << ctx.e_internalized(arg) << "\n"; + if (!ctx.e_internalized(arg)) { + ctx.internalize(arg, false); + } + } e = get_context().mk_enode(n, false, false, true); target = mk_var(e); numeral k(r); @@ -698,7 +778,7 @@ theory_var theory_diff_logic::mk_num(app* n, rational const& r) { enode* e = 0; context& ctx = get_context(); if (r.is_zero()) { - v = get_zero(n); + v = get_zero(); } else if (ctx.e_internalized(n)) { e = ctx.get_enode(n); @@ -706,7 +786,8 @@ theory_var theory_diff_logic::mk_num(app* n, rational const& r) { SASSERT(v != null_theory_var); } else { - theory_var zero = get_zero(n); + theory_var zero = get_zero(); + SASSERT(n->get_num_args() == 0); e = ctx.mk_enode(n, false, false, true); v = mk_var(e); // internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant. @@ -763,8 +844,7 @@ void theory_diff_logic::reset_eh() { dealloc(m_atoms[i]); } m_graph .reset(); - m_zero_int = null_theory_var; - m_zero_real = null_theory_var; + m_zero = null_theory_var; m_atoms .reset(); m_asserted_atoms .reset(); m_stats .reset(); @@ -775,6 +855,9 @@ void theory_diff_logic::reset_eh() { m_agility = 0.5; m_is_lia = true; m_non_diff_logic_exprs = false; + m_objectives .reset(); + m_objective_consts.reset(); + m_objective_assignments.reset(); theory::reset_eh(); } @@ -993,5 +1076,313 @@ void theory_diff_logic::get_implied_bound_antecedents(edge_id bridge_edge, m_graph.explain_subsumed_lazy(bridge_edge, subsumed_edge, f); } +template +unsigned theory_diff_logic::node2simplex(unsigned v) { + return m_objectives.size() + 2*v + 1; +} +template +unsigned theory_diff_logic::edge2simplex(unsigned e) { + return m_objectives.size() + 2*e; +} +template +unsigned theory_diff_logic::obj2simplex(unsigned e) { + return e; +} + +template +unsigned theory_diff_logic::num_simplex_vars() { + return m_objectives.size() + std::max(2*m_graph.get_num_edges(),2*m_graph.get_num_nodes()+1); +} + +template +bool theory_diff_logic::is_simplex_edge(unsigned e) { + if (e < m_objectives.size()) return false; + e -= m_objectives.size(); + return (0 == (e & 0x1)); +} + +template +unsigned theory_diff_logic::simplex2edge(unsigned e) { + SASSERT(is_simplex_edge(e)); + return (e - m_objectives.size())/2; +} + +template +void theory_diff_logic::update_simplex(Simplex& S) { + unsigned num_nodes = m_graph.get_num_nodes(); + vector > const& es = m_graph.get_all_edges(); + S.ensure_var(num_simplex_vars()); + for (unsigned i = 0; i < num_nodes; ++i) { + numeral const& a = m_graph.get_assignment(i); + rational fin = a.get_rational().to_rational(); + rational inf = a.get_infinitesimal().to_rational(); + mpq_inf q(fin.to_mpq(), inf.to_mpq()); + S.set_value(node2simplex(i), q); + } + S.set_lower(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); + S.set_upper(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); + svector vars; + unsynch_mpq_manager mgr; + scoped_mpq_vector coeffs(mgr); + coeffs.push_back(mpq(1)); + coeffs.push_back(mpq(-1)); + coeffs.push_back(mpq(-1)); + vars.resize(3); + for (unsigned i = m_num_simplex_edges; i < es.size(); ++i) { + // t - s <= w + // => + // t - s - b = 0, b >= w + dl_edge const& e = es[i]; + unsigned base_var = edge2simplex(i); + vars[0] = node2simplex(e.get_target()); + vars[1] = node2simplex(e.get_source()); + vars[2] = base_var; + S.add_row(base_var, 3, vars.c_ptr(), coeffs.c_ptr()); + } + m_num_simplex_edges = es.size(); + for (unsigned i = 0; i < es.size(); ++i) { + dl_edge const& e = es[i]; + unsigned base_var = edge2simplex(i); + if (e.is_enabled()) { + numeral const& w = e.get_weight(); + rational fin = w.get_rational().to_rational(); + rational inf = w.get_infinitesimal().to_rational(); + mpq_inf q(fin.to_mpq(),inf.to_mpq()); + S.set_upper(base_var, q); + } + else { + S.unset_upper(base_var); + } + } + for (unsigned v = m_objective_rows.size(); v < m_objectives.size(); ++v) { + unsigned w = obj2simplex(v); + objective_term const& objective = m_objectives[v]; + + // add objective function as row. + coeffs.reset(); + vars.reset(); + for (unsigned i = 0; i < objective.size(); ++i) { + coeffs.push_back(objective[i].second.to_mpq()); + vars.push_back(node2simplex(objective[i].first)); + } + coeffs.push_back(mpq(1)); + vars.push_back(w); + Simplex::row row = S.add_row(w, vars.size(), vars.c_ptr(), coeffs.c_ptr()); + m_objective_rows.push_back(row); + } +} + +template +typename theory_diff_logic::inf_eps theory_diff_logic::value(theory_var v) { + objective_term const& objective = m_objectives[v]; + inf_eps r = inf_eps(m_objective_consts[v]); + for (unsigned i = 0; i < objective.size(); ++i) { + numeral n = m_graph.get_assignment(v); + rational r1 = n.get_rational().to_rational(); + rational r2 = n.get_infinitesimal().to_rational(); + r += objective[i].second * inf_eps(rational(0), inf_rational(r1, r2)); + } + return r; +} + +template +typename theory_diff_logic::inf_eps +theory_diff_logic::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + + has_shared = false; + Simplex& S = m_S; + ast_manager& m = get_manager(); + + update_simplex(S); + objective_term const& objective = m_objectives[v]; + + TRACE("arith", + for (unsigned i = 0; i < objective.size(); ++i) { + tout << "Coefficient " << objective[i].second + << " of theory_var " << objective[i].first << "\n"; + } + tout << "Free coefficient " << m_objective_consts[v] << "\n"; + ); + + TRACE("opt", S.display(tout); display(tout);); + + // optimize + lbool is_sat = S.make_feasible(); + if (is_sat == l_undef) { + blocker = m.mk_false(); + return inf_eps::infinity(); + } + TRACE("opt", S.display(tout); ); + SASSERT(is_sat != l_false); + unsigned w = obj2simplex(v); + lbool is_fin = S.minimize(w); + switch (is_fin) { + case l_true: { + simplex::mpq_ext::eps_numeral const& val = S.get_value(w); + inf_rational r(-rational(val.first), -rational(val.second)); + Simplex::row row = m_objective_rows[v]; + TRACE("opt", tout << r << " " << "\n"; + S.display_row(tout, row, true);); + Simplex::row_iterator it = S.row_begin(row), end = S.row_end(row); + expr_ref_vector& core = m_objective_assignments[v]; + expr_ref tmp(m); + core.reset(); + for (; it != end; ++it) { + unsigned v = it->m_var; + if (is_simplex_edge(v)) { + unsigned edge_id = simplex2edge(v); + literal lit = m_graph.get_explanation(edge_id); + get_context().literal2expr(lit, tmp); + core.push_back(tmp); + } + } + compute_delta(); + for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) { + unsigned w = node2simplex(i); + simplex::mpq_ext::eps_numeral const& val = S.get_value(w); + rational r = rational(val.first) + m_delta*rational(val.second); + m_graph.set_assignment(i, numeral(r)); + } + blocker = mk_gt(v, r); + return inf_eps(rational(0), r + m_objective_consts[v]); + } + default: + TRACE("opt", tout << "unbounded\n"; ); + blocker = m.mk_false(); + return inf_eps::infinity(); + } +} + +template +theory_var theory_diff_logic::add_objective(app* term) { + objective_term objective; + theory_var result = m_objectives.size(); + rational q(1), r(0); + expr_ref_vector vr(get_manager()); + if (!is_linear(get_manager(), term)) { + result = null_theory_var; + } + else if (internalize_objective(term, q, r, objective)) { + m_objectives.push_back(objective); + m_objective_consts.push_back(r); + m_objective_assignments.push_back(vr); + } + else { + result = null_theory_var; + } + return result; +} + +template +expr_ref theory_diff_logic::mk_ineq(theory_var v, inf_rational const& val, bool is_strict) { + ast_manager& m = get_manager(); + objective_term const& t = m_objectives[v]; + expr_ref e(m), f(m), f2(m); + if (t.size() == 1 && t[0].second.is_one()) { + f = get_enode(t[0].first)->get_owner(); + } + else if (t.size() == 1 && t[0].second.is_minus_one()) { + f = m_util.mk_uminus(get_enode(t[0].first)->get_owner()); + } + else if (t.size() == 2 && t[0].second.is_one() && t[1].second.is_minus_one()) { + f = get_enode(t[0].first)->get_owner(); + f2 = get_enode(t[1].first)->get_owner(); + f = m_util.mk_sub(f, f2); + } + else if (t.size() == 2 && t[1].second.is_one() && t[0].second.is_minus_one()) { + f = get_enode(t[1].first)->get_owner(); + f2 = get_enode(t[0].first)->get_owner(); + f = m_util.mk_sub(f, f2); + } + else { + // + expr_ref_vector const& core = m_objective_assignments[v]; + f = m.mk_and(core.size(), core.c_ptr()); + if (is_strict) { + f = m.mk_not(f); + } + TRACE("arith", tout << "block: " << f << "\n";); + return f; + } + + inf_rational new_val = val; // - inf_rational(m_objective_consts[v]); + e = m_util.mk_numeral(new_val.get_rational(), m.get_sort(f)); + + if (new_val.get_infinitesimal().is_neg()) { + if (is_strict) { + f = m_util.mk_ge(f, e); + } + else { + expr_ref_vector const& core = m_objective_assignments[v]; + f = m.mk_and(core.size(), core.c_ptr()); + } + } + else { + if (is_strict) { + f = m_util.mk_gt(f, e); + } + else { + f = m_util.mk_ge(f, e); + } + } + return f; +} + +template +expr_ref theory_diff_logic::mk_gt(theory_var v, inf_rational const& val) { + return mk_ineq(v, val, true); +} + +template +expr_ref theory_diff_logic::mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val) { + return mk_ineq(v, val, false); +} + +#if 0 + context & ctx = get_context(); + model_ref mdl; + ctx.get_model(mdl); + ptr_vector formulas(ctx.get_num_asserted_formulas(), ctx.get_asserted_formulas()); + ast_manager& m = get_manager(); + model_implicant impl_extractor(m); + expr_ref_vector implicants = impl_extractor.minimize_literals(formulas, mdl); + return m.mk_and(o, m.mk_not(m.mk_and(implicants.size(), implicants.c_ptr()))); +#endif + +template +bool theory_diff_logic::internalize_objective(expr * n, rational const& m, rational& q, objective_term & objective) { + + // Compile term into objective_term format + rational r; + expr* x, *y; + if (m_util.is_numeral(n, r)) { + q += r; + } + else if (m_util.is_add(n)) { + for (unsigned i = 0; i < to_app(n)->get_num_args(); ++i) { + if (!internalize_objective(to_app(n)->get_arg(i), m, q, objective)) { + return false; + } + } + } + else if (m_util.is_mul(n, x, y) && m_util.is_numeral(x, r)) { + return internalize_objective(y, m*r, q, objective); + } + else if (m_util.is_mul(n, y, x) && m_util.is_numeral(x, r)) { + return internalize_objective(y, m*r, q, objective); + } + else if (!is_app(n)) { + return false; + } + else if (to_app(n)->get_family_id() == m_util.get_family_id()) { + return false; + } + else { + theory_var v = mk_var(to_app(n)); + objective.push_back(std::make_pair(v, m)); + } + return true; +} + #endif /* _THEORY_DIFF_LOGIC_DEF_H_ */ diff --git a/src/smt/theory_opt.cpp b/src/smt/theory_opt.cpp new file mode 100644 index 000000000..279863885 --- /dev/null +++ b/src/smt/theory_opt.cpp @@ -0,0 +1,84 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_opt.cpp + +Abstract: + + Interface utilities used by optimization providing + theory solvers. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-10-18 + +Notes: + +--*/ + +#include "arith_decl_plugin.h" +#include "smt_types.h" +#include "smt_theory.h" +#include "theory_opt.h" + +namespace smt { + bool theory_opt::is_linear(ast_manager& m, expr* term) { + arith_util a(m); + ptr_vector todo; + ast_mark mark; + todo.push_back(term); + expr* t1, *t2; + while (!todo.empty()) { + term = todo.back(); + todo.pop_back(); + if (mark.is_marked(term)) { + continue; + } + mark.mark(term, true); + if (!is_app(term)) { + return false; + } + app* t = to_app(term); + if (t->get_family_id() != a.get_family_id()) { + // done + } + else if (a.is_add(t) || a.is_to_real(t) || a.is_to_int(t) || + a.is_uminus(t) || a.is_numeral(t) || a.is_sub(t)) { + todo.append(t->get_num_args(), t->get_args()); + } + else if (a.is_mul(t, t1, t2)) { + if (is_numeral(a, t1)) { + todo.push_back(t2); + } + else if (is_numeral(a, t2)) { + todo.push_back(t1); + } + else { + return false; + } + } + else { + return false; + } + } + return true; + } + + + bool theory_opt::is_numeral(arith_util& a, expr* term) { + while (true) { + if (a.is_uminus(term) || a.is_to_real(term) || a.is_to_int(term)) { + term = to_app(term)->get_arg(0); + } + else if (a.is_numeral(term)) { + return true; + } + else { + return false; + } + } + } + +}; diff --git a/src/smt/theory_opt.h b/src/smt/theory_opt.h new file mode 100644 index 000000000..61acfc381 --- /dev/null +++ b/src/smt/theory_opt.h @@ -0,0 +1,42 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_opt.h + +Abstract: + + Interface utilities used by optimization providing + theory solvers. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-10-18 + +Notes: + +--*/ + +#include "inf_rational.h" +#include "inf_eps_rational.h" +#include "arith_decl_plugin.h" + +#ifndef _THEORY_OPT_H_ +#define _THEORY_OPT_H_ + +class filter_model_converter; +namespace smt { + class theory_opt { + public: + typedef inf_eps_rational inf_eps; + virtual inf_eps value(theory_var) = 0; + virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) = 0; + virtual theory_var add_objective(app* term) = 0; + virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val) { UNREACHABLE(); return expr_ref(*((ast_manager*)0)); } + bool is_linear(ast_manager& m, expr* term); + bool is_numeral(arith_util& a, expr* term); + }; +} + +#endif diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp new file mode 100644 index 000000000..7be53ff86 --- /dev/null +++ b/src/smt/theory_pb.cpp @@ -0,0 +1,2090 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_pb.cpp + +Abstract: + + Pseudo-Boolean theory plugin. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-05 + +Notes: + + +--*/ + +#include +#include "theory_pb.h" +#include "smt_context.h" +#include "ast_pp.h" +#include "sorting_network.h" +#include "uint_set.h" +#include "smt_model_generator.h" +#include "pb_rewriter_def.h" +#include "sparse_matrix_def.h" +#include "simplex_def.h" + + +namespace smt { + + class pb_lit_rewriter_util { + public: + typedef std::pair arg_t; + typedef vector args_t; + typedef rational numeral; + + literal negate(literal l) { + return ~l; + } + + void display(std::ostream& out, literal l) { + out << l; + } + + bool is_negated(literal l) const { + return l.sign(); + } + + bool is_true(literal l) const { + return l == true_literal; + } + + bool is_false(literal l) const { + return l == false_literal; + } + + struct compare { + bool operator()(arg_t const& a, arg_t const& b) { + return a.first < b.first; + } + }; + }; + + const unsigned theory_pb::null_index = UINT_MAX; + + + unsigned theory_pb::arg_t::get_hash() const { + return get_composite_hash(*this, size()); + } + + bool theory_pb::arg_t::operator==(arg_t const& other) const { + if (size() != other.size()) return false; + for (unsigned i = 0; i < size(); ++i) { + if (lit(i) != other.lit(i)) return false; + if (coeff(i) != other.coeff(i)) return false; + } + return true; + } + + void theory_pb::arg_t::remove_negations() { + for (unsigned i = 0; i < size(); ++i) { + if (lit(i).sign()) { + (*this)[i].first.neg(); + (*this)[i].second.neg(); + m_k += coeff(i); + } + } + } + + void theory_pb::arg_t::negate() { + numeral sum(0); + for (unsigned i = 0; i < size(); ++i) { + (*this)[i].first.neg(); + sum += coeff(i); + } + m_k = sum - m_k + numeral::one(); + VERIFY(l_undef == normalize(false)); + } + + lbool theory_pb::arg_t::normalize(bool is_eq) { + pb_lit_rewriter_util pbu; + pb_rewriter_util util(pbu); + return util.normalize(*this, m_k, is_eq); + } + + void theory_pb::arg_t::prune(bool is_eq) { + pb_lit_rewriter_util pbu; + pb_rewriter_util util(pbu); + util.prune(*this, m_k, is_eq); + } + + std::ostream& theory_pb::arg_t::display(context& ctx, std::ostream& out, bool values) const { + for (unsigned i = 0; i < size(); ++i) { + literal l(lit(i)); + if (!coeff(i).is_one()) { + out << coeff(i) << "*"; + } + out << l; + if (values) { + out << "@(" << ctx.get_assignment(l); + if (ctx.get_assignment(l) != l_undef) { + out << ":" << ctx.get_assign_level(l); + } + out << ")"; + } + if (i + 1 < size()) { + out << " + "; + } + } + out << " ~ " << k() << "\n"; + return out; + } + + app_ref theory_pb::arg_t::to_expr(bool is_eq, context& ctx, ast_manager& m) { + expr_ref tmp(m); + app_ref result(m); + svector coeffs; + expr_ref_vector args(m); + for (unsigned i = 0; i < size(); ++i) { + ctx.literal2expr(lit(i), tmp); + args.push_back(tmp); + coeffs.push_back(coeff(i)); + } + pb_util pb(m); + if (is_eq) { + result = pb.mk_eq(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k()); + } + else { + result = pb.mk_ge(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k()); + } + return result; + } + + void theory_pb::ineq::reset() { + m_max_watch.reset(); + m_watch_sz = 0; + m_watch_sum.reset(); + m_num_propagations = 0; + m_compilation_threshold = UINT_MAX; + m_compiled = l_false; + m_args[0].reset(); + m_args[0].m_k.reset(); + m_args[1].reset(); + m_args[1].m_k.reset(); + m_nfixed = 0; + m_max_sum.reset(); + m_min_sum.reset(); + } + + + void theory_pb::ineq::unique() { + pb_lit_rewriter_util pbu; + pb_rewriter_util util(pbu); + util.unique(m_args[0], m_args[0].m_k, m_is_eq); + } + + void theory_pb::ineq::post_prune() { + if (!m_args[0].empty() && is_ge()) { + m_args[0].negate(); + m_args[0].negate(); + m_args[1].reset(); + m_args[1].m_k = m_args[0].m_k; + m_args[1].append(m_args[0]); + m_args[1].negate(); + + SASSERT(m_args[0].size() == m_args[1].size()); + SASSERT(m_args[0].well_formed()); + SASSERT(m_args[1].well_formed()); + } + } + + void theory_pb::ineq::negate() { + SASSERT(!m_is_eq); + m_lit.neg(); + } + + void theory_pb::ineq::prune() { + m_args[0].prune(m_is_eq); + } + + lbool theory_pb::ineq::normalize() { + return m_args[0].normalize(m_is_eq); + } + + app_ref theory_pb::ineq::to_expr(context& ctx, ast_manager& m) { + return args().to_expr(m_is_eq, ctx, m); + } + + bool theory_pb::arg_t::well_formed() const { + SASSERT(k().is_pos()); + uint_set vars; + numeral sum = numeral::zero(); + for (unsigned i = 0; i < size(); ++i) { + SASSERT(coeff(i) <= k()); + SASSERT(numeral::one() <= coeff(i)); + SASSERT(lit(i) != true_literal); + SASSERT(lit(i) != false_literal); + SASSERT(lit(i) != null_literal); + SASSERT(!vars.contains(lit(i).var())); + vars.insert(lit(i).var()); + sum += coeff(i); + } + SASSERT(sum >= k()); + return true; + } + + theory_pb::theory_pb(ast_manager& m, theory_pb_params& p): + theory(m.mk_family_id("pb")), + m_params(p), + m_util(m), + m_max_compiled_coeff(rational(8)) + { + m_learn_complements = p.m_pb_learn_complements; + m_conflict_frequency = p.m_pb_conflict_frequency; + m_enable_compilation = p.m_pb_enable_compilation; + m_enable_simplex = p.m_pb_enable_simplex; + } + + theory_pb::~theory_pb() { + reset_eh(); + } + + theory * theory_pb::mk_fresh(context * new_ctx) { + return alloc(theory_pb, new_ctx->get_manager(), m_params); + } + + class theory_pb::remove_var : public trail { + theory_pb& pb; + unsigned v; + public: + remove_var(theory_pb& pb, unsigned v): pb(pb), v(v) {} + virtual void undo(context& ctx) { + pb.m_vars.remove(v); + pb.m_simplex.unset_lower(v); + pb.m_simplex.unset_upper(v); + } + }; + + class theory_pb::undo_bound : public trail { + theory_pb& pb; + unsigned m_v; + bool m_is_lower; + scoped_eps_numeral m_last_bound; + bool m_last_bound_valid; + literal m_last_explain; + + public: + undo_bound(theory_pb& pb, unsigned v, + bool is_lower, + scoped_eps_numeral& last_bound, + bool last_bound_valid, + literal last_explain): + pb(pb), + m_v(v), + m_is_lower(is_lower), + m_last_bound(last_bound), + m_last_bound_valid(last_bound_valid), + m_last_explain(last_explain) {} + + virtual void undo(context& ctx) { + if (m_is_lower) { + if (m_last_bound_valid) { + pb.m_simplex.set_lower(m_v, m_last_bound); + } + else { + pb.m_simplex.unset_lower(m_v); + } + pb.set_explain(pb.m_explain_lower, m_v, m_last_explain); + } + else { + if (m_last_bound_valid) { + pb.m_simplex.set_upper(m_v, m_last_bound); + } + else { + pb.m_simplex.unset_upper(m_v); + } + pb.set_explain(pb.m_explain_upper, m_v, m_last_explain); + } + m_last_bound.reset(); + } + }; + + literal theory_pb::set_explain(literal_vector& explains, unsigned var, literal expl) { + if (var >= explains.size()) { + explains.resize(var+1, null_literal); + } + literal last_explain = explains[var]; + explains[var] = expl; + return last_explain; + } + + bool theory_pb::update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound) { + if (is_lower) { + if (m_simplex.above_lower(v, bound)) { + scoped_eps_numeral last_bound(m_mpq_inf_mgr); + if (m_simplex.upper_valid(v)) { + m_simplex.get_upper(v, last_bound); + if (m_mpq_inf_mgr.gt(bound, last_bound)) { + literal lit = m_explain_upper.get(v, null_literal); + get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); + return false; + } + } + bool last_bound_valid = m_simplex.lower_valid(v); + if (last_bound_valid) { + m_simplex.get_lower(v, last_bound); + } + m_simplex.set_lower(v, bound); + literal last_explain = set_explain(m_explain_lower, v, explain); + get_context().push_trail(undo_bound(*this, v, true, last_bound, last_bound_valid, last_explain)); + } + } + else { + if (m_simplex.below_upper(v, bound)) { + scoped_eps_numeral last_bound(m_mpq_inf_mgr); + if (m_simplex.lower_valid(v)) { + m_simplex.get_lower(v, last_bound); + if (m_mpq_inf_mgr.gt(last_bound, bound)) { + literal lit = m_explain_lower.get(v, null_literal); + get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); + return false; + } + } + bool last_bound_valid = m_simplex.upper_valid(v); + if (last_bound_valid) { + m_simplex.get_upper(v, last_bound); + } + m_simplex.set_upper(v, bound); + literal last_explain = set_explain(m_explain_upper, v, explain); + get_context().push_trail(undo_bound(*this, v, false, last_bound, last_bound_valid, last_explain)); + } + } + return true; + }; + + bool theory_pb::check_feasible() { + context& ctx = get_context(); + lbool is_sat = m_simplex.make_feasible(); + if (l_false != is_sat) { + return true; + } + + row r = m_simplex.get_infeasible_row(); + // m_simplex.display_row(std::cout, r, true); + mpz const& coeff = m_simplex.get_base_coeff(r); + bool_var base_var = m_simplex.get_base_var(r); + SASSERT(m_simplex.below_lower(base_var) || m_simplex.above_upper(base_var)); + bool cant_increase = m_simplex.below_lower(base_var)?m_mpz_mgr.is_pos(coeff):m_mpz_mgr.is_neg(coeff); + + literal_vector explains; + row_iterator it = m_simplex.row_begin(r), end = m_simplex.row_end(r); + for (; it != end; ++it) { + bool_var v = it->m_var; + if (v == base_var) { + if (m_simplex.below_lower(base_var)) { + explains.push_back(m_explain_lower.get(v, null_literal)); + } + else { + explains.push_back(m_explain_upper.get(v, null_literal)); + } + } + else if (cant_increase == m_mpz_mgr.is_pos(it->m_coeff)) { + explains.push_back(m_explain_lower.get(v, null_literal)); + } + else { + explains.push_back(m_explain_upper.get(v, null_literal)); + } + } + + literal_vector lits; + for (unsigned i = 0; i < explains.size(); ++i) { + literal lit(explains[i]); + if (lit != null_literal) { + lits.push_back(~lit); + } + } + + m_stats.m_num_conflicts++; + justification* js = 0; + if (proofs_enabled()) { + js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); + } + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); + + return false; + } + + bool theory_pb::internalize_atom(app * atom, bool gate_ctx) { + SASSERT(m_util.is_at_most_k(atom) || m_util.is_le(atom) || + m_util.is_ge(atom) || m_util.is_at_least_k(atom) || + m_util.is_eq(atom)); + + context& ctx = get_context(); + if (ctx.b_internalized(atom)) { + return false; + } + + SASSERT(!ctx.b_internalized(atom)); + m_stats.m_num_predicates++; + + unsigned num_args = atom->get_num_args(); + bool_var abv = ctx.mk_bool_var(atom); + ctx.set_var_theory(abv, get_id()); + + ineq* c = alloc(ineq, m_mpz_mgr, literal(abv), m_util.is_eq(atom)); + c->m_args[0].m_k = m_util.get_k(atom); + numeral& k = c->m_args[0].m_k; + arg_t& args = c->m_args[0]; + + // extract literals and coefficients. + for (unsigned i = 0; i < num_args; ++i) { + expr* arg = atom->get_arg(i); + literal l = compile_arg(arg); + numeral c = m_util.get_coeff(atom, i); + args.push_back(std::make_pair(l, c)); + } + if (m_util.is_at_most_k(atom) || m_util.is_le(atom)) { + // turn W <= k into -W >= -k + for (unsigned i = 0; i < args.size(); ++i) { + args[i].second = -args[i].second; + } + k = -k; + } + else { + SASSERT(m_util.is_at_least_k(atom) || m_util.is_ge(atom) || m_util.is_eq(atom)); + } + TRACE("pb", display(tout, *c);); + //app_ref fml1(m), fml2(m); + //fml1 = c->to_expr(ctx, m); + c->unique(); + lbool is_true = c->normalize(); + c->prune(); + c->post_prune(); + //fml2 = c->to_expr(ctx, m); + //expr_ref validate_pb = pb_rewriter(m).mk_validate_rewrite(fml1, fml2); + //pb_rewriter(m).dump_pb_rewrite(validate_pb); + + literal lit(abv); + + + TRACE("pb", display(tout, *c); tout << " := " << lit << "\n";); + switch(is_true) { + case l_false: + lit = ~lit; + // fall-through + case l_true: + ctx.mk_th_axiom(get_id(), 1, &lit); + dealloc(c); + return true; + case l_undef: + break; + } + + if (c->k().is_one() && c->is_ge() && !m_enable_simplex) { + literal_vector& lits = get_lits(); + lits.push_back(~lit); + for (unsigned i = 0; i < c->size(); ++i) { + lits.push_back(c->lit(i)); + SASSERT(c->coeff(i).is_one()); + ctx.mk_th_axiom(get_id(), lit, ~c->lit(i)); + } + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + return true; + } + + // maximal coefficient: + scoped_mpz& max_watch = c->m_max_watch; + max_watch.reset(); + for (unsigned i = 0; i < args.size(); ++i) { + mpz const& num = args[i].second.to_mpq().numerator(); + if (m_mpz_mgr.lt(max_watch, num)) { + max_watch = num; + } + } + + // pre-compile threshold for cardinality + bool enable_compile = m_enable_compilation && c->is_ge() && !c->k().is_one(); + for (unsigned i = 0; enable_compile && i < args.size(); ++i) { + enable_compile = (args[i].second <= m_max_compiled_coeff); + } + if (enable_compile) { + unsigned log = 1, n = 1; + while (n <= args.size()) { + ++log; + n *= 2; + } + unsigned th = args.size()*log; // 10* + c->m_compilation_threshold = th; + IF_VERBOSE(2, verbose_stream() << "(smt.pb setting compilation threhshold to " << th << ")\n";); + TRACE("pb", tout << "compilation threshold: " << th << "\n";); + } + else { + c->m_compilation_threshold = UINT_MAX; + } + init_watch_var(*c); + m_ineqs.insert(abv, c); + m_ineqs_trail.push_back(abv); + + if (m_enable_simplex) { + // + // TBD: using abv as slack identity doesn't quite + // work if psuedo-Booleans are used + // in a nested way. So assume + // + + arg_t rep(c->args()); + rep.remove_negations(); // normalize representative + numeral k = rep.k(); + theory_var slack; + bool_var abv2; + TRACE("pb", display(tout << abv <<"\n", rep);); + if (m_ineq_rep.find(rep, abv2)) { + slack = abv2; + TRACE("pb", + tout << "Old row: " << abv << " |-> " << slack << " "; + tout << m_ineq_row_info.find(abv2).m_bound << " vs. " << k << "\n"; + display(tout, rep);); + } + else { + m_ineq_rep.insert(rep, abv); + svector vars; + scoped_mpz_vector coeffs(m_mpz_mgr); + for (unsigned i = 0; i < rep.size(); ++i) { + unsigned v = rep.lit(i).var(); + m_simplex.ensure_var(v); + vars.push_back(v); + if (!m_vars.contains(v)) { + mpq_inf zero(mpq(0),mpq(0)), one(mpq(1),mpq(0)); + switch(ctx.get_assignment(rep.lit(i))) { + case l_true: + VERIFY(update_bound(v, literal(v), true, one)); + m_simplex.set_lower(v, one); + break; + case l_false: + VERIFY(update_bound(v, ~literal(v), false, zero)); + m_simplex.set_upper(v, zero); + break; + default: + m_simplex.set_lower(v, zero); + m_simplex.set_upper(v, one); + break; + } + m_vars.insert(v); + ctx.push_trail(remove_var(*this, v)); + } + coeffs.push_back(rep.coeff(i).to_mpq().numerator()); + } + slack = abv; + m_simplex.ensure_var(slack); + vars.push_back(slack); + coeffs.push_back(mpz(-1)); + m_simplex.add_row(slack, vars.size(), vars.c_ptr(), coeffs.c_ptr()); + TRACE("pb", tout << "New row: " << abv << " " << k << "\n"; display(tout, rep);); + } + m_ineq_row_info.insert(abv, row_info(slack, k, rep)); + } + + TRACE("pb", display(tout, *c);); + + return true; + } + + literal theory_pb::compile_arg(expr* arg) { + context& ctx = get_context(); + ast_manager& m = get_manager(); + + bool_var bv; + bool has_bv = false; + bool negate = m.is_not(arg, arg); + SASSERT(!m.is_not(arg)); + if (!ctx.b_internalized(arg)) { + ctx.internalize(arg, false); + } + if (ctx.b_internalized(arg)) { + bv = ctx.get_bool_var(arg); + if (is_uninterp(arg) && null_theory_var == ctx.get_var_theory(bv)) { + ctx.set_var_theory(bv, get_id()); + } + has_bv = (ctx.get_var_theory(bv) == get_id()); + } + else if (m.is_true(arg)) { + bv = true_bool_var; + has_bv = true; + } + else if (m.is_false(arg)) { + bv = true_bool_var; + has_bv = true; + negate = !negate; + } + + // assumes relevancy level = 2 or 0. + // TBD: should should have been like an uninterpreted + // function internalize, where enodes for each argument + // is available. + if (!has_bv) { + expr_ref tmp(m), fml(m); + tmp = m.mk_fresh_const("pb_proxy",m.mk_bool_sort()); + fml = m.mk_iff(tmp, arg); + TRACE("pb", tout << "create proxy " << fml << "\n";); + ctx.internalize(fml, false); + SASSERT(ctx.b_internalized(tmp)); + bv = ctx.get_bool_var(tmp); + SASSERT(null_theory_var == ctx.get_var_theory(bv)); + ctx.set_var_theory(bv, get_id()); + literal lit(ctx.get_bool_var(fml)); + ctx.mk_th_axiom(get_id(), 1, &lit); + ctx.mark_as_relevant(tmp.get()); + } + return negate?~literal(bv):literal(bv); + } + + void theory_pb::del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index) { + SASSERT(c.is_ge()); + if (index < watch.size()) { + std::swap(watch[index], watch[watch.size()-1]); + } + watch.pop_back(); + + SASSERT(ineq_index < c.watch_size()); + scoped_mpz coeff(m_mpz_mgr); + coeff = c.ncoeff(ineq_index); + if (ineq_index + 1 < c.watch_size()) { + std::swap(c.args()[ineq_index], c.args()[c.watch_size()-1]); + } + --c.m_watch_sz; + c.m_watch_sum -= coeff; + if (coeff == c.max_watch()) { + coeff = c.ncoeff(0); + for (unsigned i = 1; coeff != c.max_watch() && i < c.watch_size(); ++i) { + if (coeff < c.ncoeff(i)) coeff = c.ncoeff(i); + } + c.set_max_watch(coeff); + } + + // current index of unwatched literal is c.watch_size(). + } + + void theory_pb::add_watch(ineq& c, unsigned i) { + SASSERT(c.is_ge()); + literal lit = c.lit(i); + scoped_mpz coeff(m_mpz_mgr); + coeff = c.ncoeff(i); + c.m_watch_sum += coeff; + SASSERT(i >= c.watch_size()); + + if (i > c.watch_size()) { + std::swap(c.args()[i], c.args()[c.watch_size()]); + } + ++c.m_watch_sz; + if (coeff > c.max_watch()) { + c.set_max_watch(coeff); + } + watch_literal(lit, &c); + } + + void theory_pb::watch_literal(literal lit, ineq* c) { + ptr_vector* ineqs; + if (!m_lwatch.find(lit.index(), ineqs)) { + ineqs = alloc(ptr_vector); + m_lwatch.insert(lit.index(), ineqs); + } + ineqs->push_back(c); + } + + void theory_pb::watch_var(bool_var v, ineq* c) { + ptr_vector* ineqs; + if (!m_vwatch.find(v, ineqs)) { + ineqs = alloc(ptr_vector); + m_vwatch.insert(v, ineqs); + } + ineqs->push_back(c); + } + + void theory_pb::unwatch_var(bool_var v, ineq* c) { + ptr_vector* ineqs = 0; + if (m_vwatch.find(v, ineqs)) { + remove(*ineqs, c); + } + } + + void theory_pb::unwatch_literal(literal w, ineq* c) { + ptr_vector* ineqs = 0; + if (m_lwatch.find(w.index(), ineqs)) { + remove(*ineqs, c); + } + } + + void theory_pb::remove(ptr_vector& ineqs, ineq* c) { + for (unsigned j = 0; j < ineqs.size(); ++j) { + if (ineqs[j] == c) { + std::swap(ineqs[j], ineqs[ineqs.size()-1]); + ineqs.pop_back(); + break; + } + } + } + + void theory_pb::collect_statistics(::statistics& st) const { + st.update("pb conflicts", m_stats.m_num_conflicts); + st.update("pb propagations", m_stats.m_num_propagations); + st.update("pb predicates", m_stats.m_num_predicates); + st.update("pb compilations", m_stats.m_num_compiles); + st.update("pb compiled clauses", m_stats.m_num_compiled_clauses); + st.update("pb compiled vars", m_stats.m_num_compiled_vars); + m_simplex.collect_statistics(st); + } + + void theory_pb::reset_eh() { + + // m_watch; + u_map*>::iterator it = m_lwatch.begin(), end = m_lwatch.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + it = m_vwatch.begin(), end = m_vwatch.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); + for (; itc != endc; ++itc) { + dealloc(itc->m_value); + } + m_lwatch.reset(); + m_vwatch.reset(); + m_ineqs.reset(); + m_ineqs_trail.reset(); + m_ineqs_lim.reset(); + m_stats.reset(); + m_to_compile.reset(); + } + + void theory_pb::new_eq_eh(theory_var v1, theory_var v2) { + UNREACHABLE(); + } + + final_check_status theory_pb::final_check_eh() { + TRACE("pb", display(tout);); + DEBUG_CODE(validate_final_check();); + return FC_DONE; + } + + void theory_pb::assign_eh(bool_var v, bool is_true) { + ptr_vector* ineqs = 0; + literal nlit(v, is_true); + TRACE("pb", tout << "assign: " << ~nlit << "\n";); + if (m_lwatch.find(nlit.index(), ineqs)) { + if (m_enable_simplex) { + mpq_inf num(mpq(is_true?1:0),mpq(0)); + if (!update_bound(v, ~nlit, is_true, num)) { + return; + } + + if (!check_feasible()) { + return; + } + } + + for (unsigned i = 0; i < ineqs->size(); ++i) { + ineq* c = (*ineqs)[i]; + SASSERT(c->is_ge()); + if (assign_watch_ge(v, is_true, *ineqs, i)) { + // i was removed from watch list. + --i; + } + } + } + if (m_vwatch.find(v, ineqs)) { + for (unsigned i = 0; i < ineqs->size(); ++i) { + ineq* c = (*ineqs)[i]; + assign_watch(v, is_true, *c); + } + } + ineq* c = 0; + if (m_ineqs.find(v, c)) { + if (m_enable_simplex) { + row_info const& info = m_ineq_row_info.find(v); + scoped_eps_numeral coeff(m_mpq_inf_mgr); + coeff = std::make_pair(info.m_bound.to_mpq(), mpq(0)); + unsigned slack = info.m_slack; + if (is_true) { + update_bound(slack, literal(v), true, coeff); + if (c->is_eq()) { + update_bound(slack, literal(v), false, coeff); + } + } + else if (c->is_ge()) { + m_mpq_inf_mgr.sub(coeff, std::make_pair(mpq(1),mpq(0)), coeff); + update_bound(slack, ~literal(v), false, coeff); + } + + if (!check_feasible()) { + return; + } + } + if (c->is_ge()) { + assign_ineq(*c, is_true); + } + else { + assign_eq(*c, is_true); + } + } + } + + literal_vector& theory_pb::get_all_literals(ineq& c, bool negate) { + context& ctx = get_context(); + literal_vector& lits = get_lits(); + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c.lit(i); + switch(ctx.get_assignment(l)) { + case l_true: + lits.push_back(negate?(~l):l); + break; + case l_false: + lits.push_back(negate?l:(~l)); + break; + default: + break; + } + } + return lits; + + } + + literal_vector& theory_pb::get_helpful_literals(ineq& c, bool negate) { + scoped_mpz sum(m_mpz_mgr); + mpz const& k = c.mpz_k(); + context& ctx = get_context(); + literal_vector& lits = get_lits(); + for (unsigned i = 0; sum < k && i < c.size(); ++i) { + literal l = c.lit(i); + if (ctx.get_assignment(l) == l_true) { + sum += c.ncoeff(i); + if (negate) l = ~l; + lits.push_back(l); + } + } + SASSERT(sum >= k); + return lits; + } + + literal_vector& theory_pb::get_unhelpful_literals(ineq& c, bool negate) { + context& ctx = get_context(); + literal_vector& lits = get_lits(); + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c.lit(i); + if (ctx.get_assignment(l) == l_false) { + if (negate) l = ~l; + lits.push_back(l); + } + } + return lits; + } + + class theory_pb::rewatch_vars : public trail { + theory_pb& pb; + ineq& c; + public: + rewatch_vars(theory_pb& p, ineq& c): pb(p), c(c) {} + virtual void undo(context& ctx) { + for (unsigned i = 0; i < c.size(); ++i) { + pb.watch_var(c.lit(i).var(), &c); + } + } + }; + + class theory_pb::negate_ineq : public trail { + ineq& c; + public: + negate_ineq(ineq& c): c(c) {} + virtual void undo(context& ctx) { + c.negate(); + } + }; + + /** + \brief propagate assignment to inequality. + This is a basic, non-optimized implementation based + on the assumption that inequalities are mostly units + and/or relatively few compared to number of argumets. + */ + void theory_pb::assign_ineq(ineq& c, bool is_true) { + context& ctx = get_context(); + ctx.push_trail(value_trail(c.m_max_sum)); + ctx.push_trail(value_trail(c.m_min_sum)); + ctx.push_trail(value_trail(c.m_nfixed)); + ctx.push_trail(rewatch_vars(*this, c)); + + clear_watch(c); + SASSERT(c.is_ge()); + unsigned sz = c.size(); + if (c.lit().sign() == is_true) { + c.negate(); + ctx.push_trail(negate_ineq(c)); + } + + scoped_mpz maxsum(m_mpz_mgr), mininc(m_mpz_mgr); + for (unsigned i = 0; i < sz; ++i) { + lbool asgn = ctx.get_assignment(c.lit(i)); + if (asgn != l_false) { + maxsum += c.ncoeff(i); + } + if (asgn == l_undef && (mininc.is_zero() || mininc > c.ncoeff(i))) { + mininc = c.ncoeff(i); + } + } + + TRACE("pb", + tout << "assign: " << c.lit() << "\n"; + display(tout, c); ); + + if (maxsum < c.mpz_k()) { + literal_vector& lits = get_unhelpful_literals(c, false); + lits.push_back(~c.lit()); + add_clause(c, lits); + } + else { + init_watch_literal(c); + SASSERT(c.m_watch_sum >= c.mpz_k()); + DEBUG_CODE(validate_watch(c);); + } + + // perform unit propagation + if (maxsum >= c.mpz_k() && maxsum - mininc < c.mpz_k()) { + literal_vector& lits = get_unhelpful_literals(c, true); + lits.push_back(c.lit()); + for (unsigned i = 0; i < sz; ++i) { + if (ctx.get_assignment(c.lit(i)) == l_undef) { + DEBUG_CODE(validate_assign(c, lits, c.lit(i));); + add_assign(c, lits, c.lit(i)); + } + } + } + } + + /** + \brief propagate assignment to equality. + */ + void theory_pb::assign_eq(ineq& c, bool is_true) { + SASSERT(c.is_eq()); + + } + + /** + Propagation rules: + + nfixed = N & minsum = k -> T + nfixed = N & minsum != k -> F + + minsum > k or maxsum < k -> F + minsum = k & = -> fix 0 variables + nfixed+1 = N & = -> fix unassigned variable or conflict + nfixed+1 = N & != -> maybe forced unassigned to ensure disequal + minsum >= k -> T + maxsum < k -> F + */ + + void theory_pb::assign_watch(bool_var v, bool is_true, ineq& c) { + + context& ctx = get_context(); + unsigned i; + literal l = c.lit(); + lbool asgn = ctx.get_assignment(l); + + if (c.max_sum() < c.mpz_k() && asgn == l_false) { + return; + } + if (c.is_ge() && c.min_sum() >= c.mpz_k() && asgn == l_true) { + return; + } + for (i = 0; i < c.size(); ++i) { + if (c.lit(i).var() == v) { + break; + } + } + + TRACE("pb", display(tout << "assign watch " << literal(v,!is_true) << " ", c, true);); + + SASSERT(i < c.size()); + if (c.lit(i).sign() == is_true) { + ctx.push_trail(value_trail(c.m_max_sum)); + c.m_max_sum -= c.ncoeff(i); + } + else { + ctx.push_trail(value_trail(c.m_min_sum)); + c.m_min_sum += c.ncoeff(i); + } + DEBUG_CODE( + scoped_mpz sum(m_mpz_mgr); + scoped_mpz maxs(m_mpz_mgr); + for (unsigned i = 0; i < c.size(); ++i) { + if (ctx.get_assignment(c.lit(i)) == l_true) sum += c.ncoeff(i); + if (ctx.get_assignment(c.lit(i)) != l_false) maxs += c.ncoeff(i); + } + CTRACE("pb", (maxs > c.max_sum()), display(tout, c, true);); + SASSERT(c.min_sum() <= sum); + SASSERT(sum <= maxs); + SASSERT(maxs <= c.max_sum()); + ); + SASSERT(c.min_sum() <= c.max_sum()); + SASSERT(!m_mpz_mgr.is_neg(c.min_sum())); + ctx.push_trail(value_trail(c.m_nfixed)); + ++c.m_nfixed; + SASSERT(c.nfixed() <= c.size()); + if (c.is_ge() && c.min_sum() >= c.mpz_k() && asgn != l_true) { + TRACE("pb", display(tout << "Set " << l << "\n", c, true);); + add_assign(c, get_helpful_literals(c, false), l); + } + else if (c.max_sum() < c.mpz_k() && asgn != l_false) { + TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); + add_assign(c, get_unhelpful_literals(c, true), ~l); + } + else if (c.is_eq() && c.nfixed() == c.size() && c.min_sum() == c.mpz_k() && asgn != l_true) { + TRACE("pb", display(tout << "Set " << l << "\n", c, true);); + add_assign(c, get_all_literals(c, false), l); + } + else if (c.is_eq() && c.nfixed() == c.size() && c.min_sum() != c.mpz_k() && asgn != l_false) { + TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); + add_assign(c, get_all_literals(c, false), ~l); + } +#if 0 + else if (c.is_eq() && c.min_sum() > c.mpz_k() && asgn != l_false) { + TRACE("pb", display(tout << "Set " << ~l << "\n", c, true);); + add_assign(c, get_all_literals(c, false), ~l); + } + else if (c.is_eq() && asgn == l_true && c.min_sum() == c.mpz_k() && c.max_sum() > c.mpz_k()) { + literal_vector& lits = get_all_literals(c, false); + lits.push_back(c.lit()); + for (unsigned i = 0; i < c.size(); ++i) { + if (ctx.get_assignment(c.lit(i)) == l_undef) { + add_assign(c, lits, ~c.lit(i)); + } + } + } +#endif + else { + IF_VERBOSE(3, display(verbose_stream() << "no propagation ", c, true);); + } + } + + + /** + \brief v is assigned in inequality c. Update current bounds and watch list. + Optimize for case where the c.lit() is True. This covers the case where + inequalities are unit literals and formulas in negation normal form + (inequalities are closed under negation). + */ + bool theory_pb::assign_watch_ge(bool_var v, bool is_true, watch_list& watch, unsigned watch_index) { + bool removed = false; + context& ctx = get_context(); + ineq& c = *watch[watch_index]; + //display(std::cout << v << " ", c, true); + unsigned w = c.find_lit(v, 0, c.watch_size()); + SASSERT(ctx.get_assignment(c.lit()) == l_true); + SASSERT(is_true == c.lit(w).sign()); + + // + // watch_sum is decreased. + // Adjust set of watched literals. + // + + scoped_mpz k_coeff(m_mpz_mgr), k(m_mpz_mgr); + k = c.mpz_k(); + k_coeff = k; + k_coeff += c.ncoeff(w); + bool add_more = c.watch_sum() < k_coeff + c.max_watch(); + for (unsigned i = c.watch_size(); add_more && i < c.size(); ++i) { + if (ctx.get_assignment(c.lit(i)) != l_false) { + add_watch(c, i); + add_more = c.watch_sum() < k_coeff + c.max_watch(); + } + } + + if (c.watch_sum() < k_coeff) { + // + // L: 3*x1 + 2*x2 + x4 >= 3, but x1 <- 0, x2 <- 0 + // create clause x1 or x2 or ~L + // + literal_vector& lits = get_unhelpful_literals(c, false); + lits.push_back(~c.lit()); + add_clause(c, lits); + } + else { + del_watch(watch, watch_index, c, w); + removed = true; + SASSERT(c.watch_sum() >= k); + if (c.watch_sum() < k + c.max_watch()) { + + // + // opportunities for unit propagation for unassigned + // literals whose coefficients satisfy + // c.watch_sum() < k + // + // L: 3*x1 + 2*x2 + x4 >= 3, but x1 <- 0 + // Create clauses x1 or ~L or x2 + // x1 or ~L or x4 + // + + literal_vector& lits = get_unhelpful_literals(c, true); + lits.push_back(c.lit()); + scoped_mpz deficit(m_mpz_mgr); + deficit = c.watch_sum() - k; + for (unsigned i = 0; i < c.size(); ++i) { + if (ctx.get_assignment(c.lit(i)) == l_undef && deficit < c.ncoeff(i)) { + DEBUG_CODE(validate_assign(c, lits, c.lit(i));); + add_assign(c, lits, c.lit(i)); + // break; + } + } + } + // + // else: c.watch_sum() >= k + c.max_watch() + // + } + + TRACE("pb", + tout << "assign: " << literal(v,!is_true) << "\n"; + display(tout, c); ); + + + return removed; + } + + struct theory_pb::psort_expr { + context& ctx; + ast_manager& m; + theory_pb& th; + typedef smt::literal literal; + typedef smt::literal_vector literal_vector; + + psort_expr(context& c, theory_pb& th): + ctx(c), + m(c.get_manager()), + th(th) {} + + literal fresh() { + app_ref y(m); + y = m.mk_fresh_const("y", m.mk_bool_sort()); + return literal(ctx.mk_bool_var(y)); + } + + literal mk_max(literal a, literal b) { + if (a == b) return a; + expr_ref t1(m), t2(m), t3(m); + ctx.literal2expr(a, t1); + ctx.literal2expr(b, t2); + t3 = m.mk_or(t1, t2); + bool_var v = ctx.b_internalized(t3)?ctx.get_bool_var(t3):ctx.mk_bool_var(t3); + return literal(v); + } + + literal mk_min(literal a, literal b) { + if (a == b) return a; + expr_ref t1(m), t2(m), t3(m); + ctx.literal2expr(a, t1); + ctx.literal2expr(b, t2); + t3 = m.mk_and(t1, t2); + bool_var v = ctx.b_internalized(t3)?ctx.get_bool_var(t3):ctx.mk_bool_var(t3); + return literal(v); + } + + literal mk_not(literal a) { return ~a; } + + void mk_clause(unsigned n, literal const* ls) { + literal_vector tmp(n, ls); + ctx.mk_clause(n, tmp.c_ptr(), th.justify(tmp), CLS_AUX, 0); + } + + literal mk_false() { return false_literal; } + literal mk_true() { return true_literal; } + + std::ostream& pp(std::ostream& out, literal l) { return out << l; } + + }; + + // for testing + literal theory_pb::assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs) { + theory_pb_params p; + theory_pb th(ctx.get_manager(), p); + psort_expr ps(ctx, th); + psort_nw sort(ps); + return sort.ge(false, k, n, xs); + } + + + void theory_pb::inc_propagations(ineq& c) { + ++c.m_num_propagations; + if (c.m_compiled == l_false && c.m_num_propagations > c.m_compilation_threshold) { + c.m_compiled = l_undef; + m_to_compile.push_back(&c); + } + } + + void theory_pb::restart_eh() { + for (unsigned i = 0; i < m_to_compile.size(); ++i) { + compile_ineq(*m_to_compile[i]); + } + m_to_compile.reset(); + } + + + void theory_pb::compile_ineq(ineq& c) { + ++m_stats.m_num_compiles; + context& ctx = get_context(); + // only cardinality constraints are compiled. + SASSERT(c.m_compilation_threshold < UINT_MAX); + DEBUG_CODE(for (unsigned i = 0; i < c.size(); ++i) SASSERT(c.coeff(i).is_int()); ); + unsigned k = c.k().get_unsigned(); + unsigned num_args = c.size(); + + + literal thl = c.lit(); + literal at_least_k; + + literal_vector in; + for (unsigned i = 0; i < num_args; ++i) { + rational n = c.coeff(i); + lbool val = ctx.get_assignment(c.lit()); + if (val != l_undef && + ctx.get_assign_level(thl) == ctx.get_base_level()) { + if (val == l_true) { + unsigned m = n.get_unsigned(); + if (k < m) { + return; + } + k -= m; + } + continue; + } + while (n.is_pos()) { + in.push_back(c.lit(i)); + n -= rational::one(); + } + } + if (ctx.get_assignment(thl) == l_true && + ctx.get_assign_level(thl) == ctx.get_base_level()) { + psort_expr ps(ctx, *this); + psort_nw sortnw(ps); + sortnw.m_stats.reset(); + at_least_k = sortnw.ge(false, k, in.size(), in.c_ptr()); + ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k)); + m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars; + m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses; + } + else { + psort_expr ps(ctx, *this); + psort_nw sortnw(ps); + sortnw.m_stats.reset(); + literal at_least_k = sortnw.ge(true, k, in.size(), in.c_ptr()); + ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k)); + ctx.mk_clause(~at_least_k, thl, justify(thl, ~at_least_k)); + m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars; + m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses; + } + IF_VERBOSE(1, verbose_stream() + << "(smt.pb compile sorting network bound: " + << k << " literals: " << in.size() << ")\n";); + + TRACE("pb", tout << thl << "\n";); + // auxiliary clauses get removed when popping scopes. + // we have to recompile the circuit after back-tracking. + c.m_compiled = l_false; + ctx.push_trail(value_trail(c.m_compiled)); + c.m_compiled = l_true; + } + + + void theory_pb::init_search_eh() { + m_to_compile.reset(); + } + + void theory_pb::push_scope_eh() { + m_ineqs_lim.push_back(m_ineqs_trail.size()); + } + + void theory_pb::pop_scope_eh(unsigned num_scopes) { + + // remove inequalities. + unsigned new_lim = m_ineqs_lim.size()-num_scopes; + unsigned sz = m_ineqs_lim[new_lim]; + while (m_ineqs_trail.size() > sz) { + bool_var v = m_ineqs_trail.back(); + ineq* c = 0; + VERIFY(m_ineqs.find(v, c)); + clear_watch(*c); + m_ineqs.remove(v); + m_ineqs_trail.pop_back(); + if (m_enable_simplex) { + row_info r_info; + VERIFY(m_ineq_row_info.find(v, r_info)); + m_ineq_row_info.erase(v); + bool_var v2 = m_ineq_rep.find(r_info.m_rep); + if (v == v2) { + m_simplex.del_row(r_info.m_slack); + m_ineq_rep.erase(r_info.m_rep); + } + } + dealloc(c); + } + m_ineqs_lim.resize(new_lim); + } + + void theory_pb::clear_watch(ineq& c) { + for (unsigned i = 0; i < c.size(); ++i) { + literal w = c.lit(i); + unwatch_var(w.var(), &c); + unwatch_literal(w, &c); + } + c.m_watch_sum.reset(); + c.m_watch_sz = 0; + c.m_max_watch.reset(); + c.m_nfixed = 0; + c.m_max_sum.reset(); + c.m_min_sum.reset(); + } + + class theory_pb::unwatch_ge : public trail { + theory_pb& pb; + ineq& c; + public: + unwatch_ge(theory_pb& p, ineq& c): pb(p), c(c) {} + + virtual void undo(context& ctx) { + for (unsigned i = 0; i < c.watch_size(); ++i) { + pb.unwatch_literal(c.lit(i), &c); + } + c.m_watch_sz = 0; + c.m_watch_sum.reset(); + c.m_max_watch.reset(); + } + }; + + + void theory_pb::init_watch_literal(ineq& c) { + context& ctx = get_context(); + scoped_mpz max_k(m_mpz_mgr); + c.m_watch_sum.reset(); + c.m_watch_sz = 0; + c.m_max_watch.reset(); + bool watch_more = true; + for (unsigned i = 0; watch_more && i < c.size(); ++i) { + if (ctx.get_assignment(c.lit(i)) != l_false) { + add_watch(c, i); + max_k = c.mpz_k(); + max_k += c.max_watch(); + watch_more = c.m_watch_sum < max_k; + } + } + ctx.push_trail(unwatch_ge(*this, c)); + } + + void theory_pb::init_watch_var(ineq& c) { + c.m_min_sum.reset(); + c.m_max_sum.reset(); + c.m_nfixed = 0; + c.m_watch_sum.reset(); + c.m_max_watch.reset(); + c.m_watch_sz = 0; + for (unsigned i = 0; i < c.size(); ++i) { + watch_var(c.lit(i).var(), &c); + c.m_max_sum += c.ncoeff(i); + } + } + + literal_vector& theory_pb::get_lits() { + m_literals.reset(); + return m_literals; + } + + class theory_pb::pb_justification : public theory_propagation_justification { + ineq& m_ineq; + public: + pb_justification(ineq& c, family_id fid, region & r, + unsigned num_lits, literal const * lits, literal consequent): + theory_propagation_justification(fid, r, num_lits, lits, consequent), + m_ineq(c) + {} + ineq& get_ineq() { return m_ineq; } + }; + + void theory_pb::add_assign(ineq& c, literal_vector const& lits, literal l) { + inc_propagations(c); + m_stats.m_num_propagations++; + context& ctx = get_context(); + TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - "; + for (unsigned i = 0; i < lits.size(); ++i) { + tout << lits[i] << " "; + } + tout << "=> " << l << "\n"; + display(tout, c, true);); + + ctx.assign(l, ctx.mk_justification( + pb_justification( + c, get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), l))); + } + + + + void theory_pb::add_clause(ineq& c, literal_vector const& lits) { + inc_propagations(c); + m_stats.m_num_conflicts++; + context& ctx = get_context(); +#if 0 + if (m_stats.m_num_conflicts == 1000) { + display(std::cout); + } +#endif + TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - "; + for (unsigned i = 0; i < lits.size(); ++i) { + tout << lits[i] << " "; + } + tout << "\n"; + display(tout, c, true);); + + justification* js = 0; + + if (m_conflict_frequency == 0 || (m_conflict_frequency -1 == (c.m_num_propagations % m_conflict_frequency))) { + resolve_conflict(c); + } + if (proofs_enabled()) { + js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); + } + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); + } + + + void theory_pb::set_mark(bool_var v, unsigned idx) { + SASSERT(v != null_bool_var); + if (v >= static_cast(m_conseq_index.size())) { + m_conseq_index.resize(v+1, null_index); + } + SASSERT(!is_marked(v) || m_conseq_index[v] == idx); + m_marked.push_back(v); + m_conseq_index[v] = idx; + } + + bool theory_pb::is_marked(bool_var v) const { + return + (v < static_cast(m_conseq_index.size())) && + (m_conseq_index[v] != null_index); + } + + void theory_pb::unset_mark(bool_var v) { + SASSERT(v != null_bool_var); + if (v < static_cast(m_conseq_index.size())) { + m_conseq_index[v] = null_index; + } + } + + void theory_pb::unset_marks() { + for (unsigned i = 0; i < m_marked.size(); ++i) { + unset_mark(m_marked[i]); + } + m_marked.reset(); + } + + void theory_pb::process_antecedent(literal l, numeral coeff) { + context& ctx = get_context(); + bool_var v = l.var(); + unsigned lvl = ctx.get_assign_level(v); + + if (ctx.get_assignment(l) != l_false) { + m_lemma.m_k -= coeff; + if (m_learn_complements && is_marked(v)) { + SASSERT(ctx.get_assignment(l) == l_true); + numeral& lcoeff = m_lemma[m_conseq_index[v]].second; + lcoeff -= coeff; + if (!lcoeff.is_pos()) { + // perhaps let lemma simplification change coefficient + // when negative? + remove_from_lemma(m_conseq_index[v]); + } + } + } + else if (lvl > ctx.get_base_level()) { + if (is_marked(v)) { + m_lemma[m_conseq_index[v]].second += coeff; + SASSERT(m_lemma[m_conseq_index[v]].second.is_pos()); + } + else { + if (lvl == m_conflict_lvl) { + TRACE("pb", tout << "add mark: " << l << " " << coeff << "\n";); + ++m_num_marks; + } + set_mark(v, m_lemma.size()); + m_lemma.push_back(std::make_pair(l, coeff)); + } + TRACE("pb_verbose", tout + << "ante: " << m_lemma.lit(m_conseq_index[v]) << "*" + << m_lemma.coeff(m_conseq_index[v]) << " " << lvl << "\n";); + } + } + + void theory_pb::process_ineq(ineq& c, literal conseq, numeral coeff1) { + + // + // Create CUT. + // + + // + // . find coeff2 + // . find lcm of coefficients to conseq. + // . multiply m_lemma by lcm/coeff coefficient to align. + // . create lcm/coeff_2 to multiply on this side. + // . cut resolve constraints. + // + + context& ctx = get_context(); + numeral coeff2 = (conseq==null_literal)?numeral::one():numeral::zero(); + for (unsigned i = 0; i < c.size(); ++i) { + if (c.lit(i) == conseq) { + coeff2 = c.coeff(i); + break; + } + } + SASSERT(coeff2.is_pos()); + numeral lc = lcm(coeff1, coeff2); + numeral g = lc/coeff1; + SASSERT(g.is_int()); + if (g > numeral::one()) { + for (unsigned i = 0; i < m_lemma.size(); ++i) { + m_lemma[i].second *= g; + } + m_lemma.m_k *= g; + } + g = lc/coeff2; + SASSERT(g.is_int()); + m_lemma.m_k += g*c.k(); + + for (unsigned i = 0; i < c.size(); ++i) { + process_antecedent(c.lit(i), g*c.coeff(i)); + } + + SASSERT(ctx.get_assignment(c.lit()) == l_true); + if (ctx.get_assign_level(c.lit()) > ctx.get_base_level()) { + m_ineq_literals.push_back(c.lit()); + } + } + + // + // modeled after sat_solver/smt_context + // + bool theory_pb::resolve_conflict(ineq& c) { + + if (!c.is_ge()) { + return false; + } + TRACE("pb", display(tout, c, true);); + + bool_var v; + literal conseq; + context& ctx = get_context(); + unsigned& lvl = m_conflict_lvl = 0; + for (unsigned i = 0; i < c.size(); ++i) { + if (ctx.get_assignment(c.lit(i)) == l_false) { + lvl = std::max(lvl, ctx.get_assign_level(c.lit(i))); + } + } + if (lvl < ctx.get_assign_level(c.lit()) || lvl == ctx.get_base_level()) { + return false; + } + + unset_marks(); + m_num_marks = 0; + m_lemma.reset(); + m_lemma.m_k.reset(); + m_ineq_literals.reset(); + process_ineq(c, null_literal, numeral::one()); // add consequent to lemma. + + // point into stack of assigned literals + literal_vector const& lits = ctx.assigned_literals(); + SASSERT(!lits.empty()); + unsigned idx = lits.size()-1; + + while (m_num_marks > 0) { + + TRACE("pb_verbose", display(tout << "lemma ", m_lemma);); + + lbool is_sat = m_lemma.normalize(false); + if (is_sat == l_false) { + break; + } + if (is_sat == l_true) { + IF_VERBOSE(0, verbose_stream() << "lemma already evaluated\n";); + TRACE("pb", tout << "lemma already evaluated\n";); + return false; + } + TRACE("pb", display(tout, m_lemma);); + SASSERT(m_lemma.well_formed()); + + // + // find the next marked variable in the assignment stack + // + do { + conseq = lits[idx]; + v = conseq.var(); + --idx; + } + while (!is_marked(v) && idx > 0); + if (idx == 0 && !is_marked(v)) { + // + // Yes, this can (currently) happen because + // the decisions for performing unit propagation + // are made asynchronously. + // In other words, PB unit propagation does not follow the + // same order as the assignment stack. + // It is not a correctness bug but causes to miss lemmas. + // + IF_VERBOSE(2, display_resolved_lemma(verbose_stream());); + TRACE("pb", display_resolved_lemma(tout);); + return false; + } + + unsigned conseq_index = m_conseq_index[v]; + numeral conseq_coeff = m_lemma.coeff(conseq_index); + + TRACE("pb", display(tout, m_lemma, true); + tout << "conseq: " << conseq << " at index: " << conseq_index << "\n";); + + SASSERT(~conseq == m_lemma.lit(conseq_index)); + + remove_from_lemma(conseq_index); + + b_justification js = ctx.get_justification(v); + + // + // Resolve selected conseq with antecedents. + // + + switch(js.get_kind()) { + + case b_justification::CLAUSE: { + clause& cls = *js.get_clause(); + justification* cjs = cls.get_justification(); + if (cjs && !is_proof_justification(*cjs)) { + TRACE("pb", tout << "skipping justification for clause over: " << conseq << " " + << typeid(*cjs).name() << "\n";); + m_ineq_literals.push_back(conseq); + break; + } + unsigned num_lits = cls.get_num_literals(); + if (cls.get_literal(0) == conseq) { + process_antecedent(cls.get_literal(1), conseq_coeff); + } + else { + SASSERT(cls.get_literal(1) == conseq); + process_antecedent(cls.get_literal(0), conseq_coeff); + } + for (unsigned i = 2; i < num_lits; ++i) { + process_antecedent(cls.get_literal(i), conseq_coeff); + } + TRACE("pb", for (unsigned i = 0; i < num_lits; ++i) tout << cls.get_literal(i) << " "; tout << "\n";); + break; + } + case b_justification::BIN_CLAUSE: + process_antecedent(~js.get_literal(), conseq_coeff); + TRACE("pb", tout << "binary: " << js.get_literal() << "\n";); + break; + case b_justification::AXIOM: + if (ctx.get_assign_level(v) > ctx.get_base_level()) { + m_ineq_literals.push_back(conseq); + } + TRACE("pb", tout << "axiom " << conseq << "\n";); + break; + case b_justification::JUSTIFICATION: { + justification* j = js.get_justification(); + pb_justification* pbj = 0; + + if (!conseq.sign() && j->get_from_theory() == get_id()) { + pbj = dynamic_cast(j); + } + if (pbj && pbj->get_ineq().is_eq()) { + // only resolve >= that are positive consequences. + pbj = 0; + } + if (pbj && pbj->get_ineq().lit() == conseq) { + // can't resolve against literal representing inequality. + pbj = 0; + } + if (pbj) { + // weaken the lemma and resolve. + TRACE("pb", display(tout << "resolve with inequality", pbj->get_ineq(), true);); + process_ineq(pbj->get_ineq(), conseq, conseq_coeff); + } + else { + TRACE("pb", tout << "skipping justification for " << conseq + << " from theory " << j->get_from_theory() << " " + << typeid(*j).name() << "\n";); + m_ineq_literals.push_back(conseq); + } + break; + } + default: + UNREACHABLE(); + } + } + + TRACE("pb", + for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { + tout << m_ineq_literals[i] << " "; + } + display(tout << "=> ", m_lemma);); + + // 3x + 3y + z + u >= 4 + // ~x /\ ~y => z + u >= + + IF_VERBOSE(4, display(verbose_stream() << "lemma1: ", m_lemma);); + hoist_maximal_values(); + lbool is_true = m_lemma.normalize(false); + m_lemma.prune(false); + + IF_VERBOSE(4, display(verbose_stream() << "lemma2: ", m_lemma);); + //unsigned l_size = m_ineq_literals.size() + ((is_true==l_false)?0:m_lemma.size()); + //if (s_min_l_size >= l_size) { + // verbose_stream() << "(pb.conflict min size: " << l_size << ")\n"; + // s_min_l_size = l_size; + //} + //IF_VERBOSE(1, verbose_stream() << "(pb.conflict " << m_ineq_literals.size() << " " << m_lemma.size() << "\n";); + switch(is_true) { + case l_true: + UNREACHABLE(); + return false; + case l_false: + inc_propagations(c); + m_stats.m_num_conflicts++; + for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { + m_ineq_literals[i].neg(); + } + ctx.mk_clause(m_ineq_literals.size(), m_ineq_literals.c_ptr(), justify(m_ineq_literals), CLS_AUX_LEMMA, 0); + break; + default: { + app_ref tmp = m_lemma.to_expr(false, ctx, get_manager()); + internalize_atom(tmp, false); + ctx.mark_as_relevant(tmp.get()); + literal l(ctx.get_bool_var(tmp)); + add_assign(c, m_ineq_literals, l); + break; + } + } + return true; + } + + bool theory_pb::is_proof_justification(justification const& j) const { + return typeid(smt::justification_proof_wrapper) == typeid(j); + } + + justification* theory_pb::justify(literal l1, literal l2) { + literal lits[2] = { l1, l2 }; + justification* js = 0; + if (proofs_enabled()) { + js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), 2, lits)); + } + return js; + } + + justification* theory_pb::justify(literal_vector const& lits) { + justification* js = 0; + if (proofs_enabled()) { + js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), lits.size(), lits.c_ptr())); + } + return js; + } + + void theory_pb::hoist_maximal_values() { + for (unsigned i = 0; i < m_lemma.size(); ++i) { + if (m_lemma.coeff(i) >= m_lemma.k()) { + m_ineq_literals.push_back(~m_lemma.lit(i)); + std::swap(m_lemma[i], m_lemma[m_lemma.size()-1]); + m_lemma.pop_back(); + --i; + } + } + } + + void theory_pb::remove_from_lemma(unsigned idx) { + // Remove conseq from lemma: + literal lit = m_lemma.lit(idx); + unsigned last = m_lemma.size()-1; + if (idx != last) { + m_lemma[idx] = m_lemma[last]; + m_conseq_index[m_lemma.lit(idx).var()] = idx; + } + m_lemma.pop_back(); + unset_mark(lit.var()); + --m_num_marks; + } + + // debug methods + + void theory_pb::validate_watch(ineq const& c) const { + scoped_mpz sum(m_mpz_mgr), max(m_mpz_mgr); + for (unsigned i = 0; i < c.watch_size(); ++i) { + sum += c.ncoeff(i); + if (max < c.ncoeff(i)) { + max = c.ncoeff(i); + } + } + SASSERT(c.watch_sum() == sum); + SASSERT(sum >= c.mpz_k()); + SASSERT(max == c.max_watch()); + } + + void theory_pb::validate_assign(ineq const& c, literal_vector const& lits, literal l) const { + uint_set nlits; + context& ctx = get_context(); + for (unsigned i = 0; i < lits.size(); ++i) { + SASSERT(ctx.get_assignment(lits[i]) == l_true); + nlits.insert((~lits[i]).index()); + } + SASSERT(ctx.get_assignment(l) == l_undef); + SASSERT(ctx.get_assignment(c.lit()) == l_true); + nlits.insert(l.index()); + numeral sum = numeral::zero(); + for (unsigned i = 0; i < c.size(); ++i) { + literal lit = c.lit(i); + if (!nlits.contains(lit.index())) { + sum += c.coeff(i); + } + } + CTRACE("pb", (sum >= c.k()), + display(tout << "invalid assign" , c, true); + for (unsigned i = 0; i < lits.size(); ++i) { + tout << lits[i] << " "; + } + tout << " => " << l << "\n";); + SASSERT(sum < c.k()); + } + + void theory_pb::validate_final_check() { + u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); + for (; itc != endc; ++itc) { + validate_final_check(*itc->m_value); + } + } + + void theory_pb::validate_final_check(ineq& c) { + context& ctx = get_context(); + + if (ctx.get_assignment(c.lit()) == l_undef) { + return; + } + if (!ctx.is_relevant(c.lit())) { + return; + } + numeral sum = numeral::zero(), maxsum = numeral::zero(); + for (unsigned i = 0; i < c.size(); ++i) { + switch(ctx.get_assignment(c.lit(i))) { + case l_true: + sum += c.coeff(i); + case l_undef: + maxsum += c.coeff(i); + break; + case l_false: + break; + } + } + TRACE("pb", display(tout << "validate: ", c, true); + tout << "sum: " << sum << " " << maxsum << " "; + tout << ctx.get_assignment(c.lit()) << "\n"; + ); + + SASSERT(sum <= maxsum); + SASSERT(!c.is_ge() || (sum >= c.k()) == (ctx.get_assignment(c.lit()) == l_true)); + SASSERT(!c.is_ge() || (maxsum < c.k()) == (ctx.get_assignment(c.lit()) == l_false)); + SASSERT(!c.is_eq() || (sum == c.k()) == (ctx.get_assignment(c.lit()) == l_true)); + } + + // display methods + + void theory_pb::display_resolved_lemma(std::ostream& out) const { + context& ctx = get_context(); + literal_vector const& lits = ctx.assigned_literals(); + bool_var v; + unsigned lvl; + out << "num marks: " << m_num_marks << "\n"; + out << "conflict level: " << m_conflict_lvl << "\n"; + for (unsigned i = 0; i < lits.size(); ++i) { + v = lits[i].var(); + lvl = ctx.get_assign_level(v); + out << lits[i] + << "@ " << lvl + << " " << (is_marked(v)?"m":"u") + << "\n"; + + if (lvl == m_conflict_lvl && is_marked(v)) { + out << "skipped: " << lits[i] << ":"<< i << "\n"; + } + } + display(out, m_lemma, true); + + unsigned nc = 0; + for (unsigned i = 0; i < m_lemma.size(); ++i) { + v = m_lemma.lit(i).var(); + lvl = ctx.get_assign_level(v); + if (lvl == m_conflict_lvl) ++nc; + out << m_lemma.lit(i) + << "@" << lvl + << " " << (is_marked(v)?"m":"u") + << " " << ctx.get_assignment(m_lemma.lit(i)) + << "\n"; + } + out << "num conflicts: " << nc << "\n"; + } + + std::ostream& theory_pb::display(std::ostream& out, arg_t const& c, bool values) const { + return c.display(get_context(), out, values); + } + + std::ostream& theory_pb::display(std::ostream& out, ineq const& c, bool values) const { + ast_manager& m = get_manager(); + context& ctx = get_context(); + out << c.lit(); + if (c.lit() != null_literal) { + if (values) { + out << "@(" << ctx.get_assignment(c.lit()); + if (ctx.get_assignment(c.lit()) != l_undef) { + out << ":" << ctx.get_assign_level(c.lit()); + } + out << ")"; + } + expr_ref tmp(m); + ctx.literal2expr(c.lit(), tmp); + out << " " << tmp << "\n"; + } + else { + out << " "; + } + for (unsigned i = 0; i < c.size(); ++i) { + literal l(c.lit(i)); + if (!c.coeff(i).is_one()) { + out << c.coeff(i) << "*"; + } + out << l; + if (values) { + out << "@(" << ctx.get_assignment(l); + if (ctx.get_assignment(l) != l_undef) { + out << ":" << ctx.get_assign_level(l); + } + out << ")"; + } + if (i + 1 == c.watch_size()) { + out << " .w "; + } + if (i + 1 < c.size()) { + out << " + "; + } + } + out << (c.is_ge()?" >= ":" = ") << c.k() << "\n"; + if (c.m_num_propagations) out << "propagations: " << c.m_num_propagations << " "; + if (c.m_max_watch.is_pos()) out << "max_watch: " << c.max_watch() << " "; + if (c.watch_size()) out << "watch size: " << c.watch_size() << " "; + if (c.m_watch_sum.is_pos()) out << "watch-sum: " << c.watch_sum() << " "; + if (!c.m_max_sum.is_zero()) out << "sum: [" << c.min_sum() << ":" << c.max_sum() << "] "; + if (c.m_num_propagations || c.m_max_watch.is_pos() || c.watch_size() || + c.m_watch_sum.is_pos() || !c.m_max_sum.is_zero()) out << "\n"; + return out; + } + + class theory_pb::pb_model_value_proc : public model_value_proc { + app* m_app; + svector m_dependencies; + public: + + pb_model_value_proc(app* a): + m_app(a) {} + + void add(enode* n) { + m_dependencies.push_back(model_value_dependency(n)); + } + + virtual void get_dependencies(buffer & result) { + result.append(m_dependencies.size(), m_dependencies.c_ptr()); + } + + virtual app * mk_value(model_generator & mg, ptr_vector & values) { + ast_manager& m = mg.get_manager(); + SASSERT(values.size() == m_dependencies.size()); + SASSERT(values.size() == m_app->get_num_args()); + pb_util u(m); + rational sum(0); + for (unsigned i = 0; i < m_app->get_num_args(); ++i) { + if (!m.is_true(values[i]) && !m.is_false(values[i])) { + return m_app; + } + if (m.is_true(values[i])) { + sum += u.get_coeff(m_app, i); + } + } + rational k = u.get_k(m_app); + switch(m_app->get_decl_kind()) { + case OP_AT_MOST_K: + return (sum <= k)?m.mk_true():m.mk_false(); + case OP_AT_LEAST_K: + return (sum >= k)?m.mk_true():m.mk_false(); + case OP_PB_LE: + return (sum <= k)?m.mk_true():m.mk_false(); + case OP_PB_GE: + return (sum >= k)?m.mk_true():m.mk_false(); + default: + UNREACHABLE(); + return 0; + } + return 0; + } + }; + + class pb_factory : public value_factory { + public: + pb_factory(ast_manager& m, family_id fid): + value_factory(m, fid) {} + + virtual expr * get_some_value(sort * s) { + return m_manager.mk_true(); + } + virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { + v1 = m_manager.mk_true(); + v2 = m_manager.mk_false(); + return true; + } + virtual expr * get_fresh_value(sort * s) { + return 0; + } + virtual void register_value(expr * n) { } + }; + + void theory_pb::init_model(model_generator & m) { + m.register_factory(alloc(pb_factory, get_manager(), get_id())); + } + + model_value_proc * theory_pb::mk_value(enode * n, model_generator & mg) { + context& ctx = get_context(); + app* a = n->get_owner(); + pb_model_value_proc* p = alloc(pb_model_value_proc, a); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + p->add(ctx.get_enode(a->get_arg(i))); + } + return p; + } + + void theory_pb::display(std::ostream& out) const { + u_map*>::iterator it = m_lwatch.begin(), end = m_lwatch.end(); + for (; it != end; ++it) { + out << "watch: " << to_literal(it->m_key) << " |-> "; + watch_list const& wl = *it->m_value; + for (unsigned i = 0; i < wl.size(); ++i) { + out << wl[i]->lit() << " "; + } + out << "\n"; + } + it = m_vwatch.begin(), end = m_vwatch.end(); + for (; it != end; ++it) { + out << "watch (v): " << literal(it->m_key) << " |-> "; + watch_list const& wl = *it->m_value; + for (unsigned i = 0; i < wl.size(); ++i) { + out << wl[i]->lit() << " "; + } + out << "\n"; + } + u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); + for (; itc != endc; ++itc) { + ineq& c = *itc->m_value; + display(out, c, true); + } + } + + +} diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h new file mode 100644 index 000000000..b9fddba38 --- /dev/null +++ b/src/smt/theory_pb.h @@ -0,0 +1,322 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_pb.h + +Abstract: + + Cardinality theory plugin. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-05 + +Notes: + + This custom theory handles cardinality constraints + It performs unit propagation and switches to creating + sorting circuits if it keeps having to propagate (create new clauses). +--*/ + +#include "smt_theory.h" +#include "pb_decl_plugin.h" +#include "smt_clause.h" +#include "theory_pb_params.h" +#include "simplex.h" + +namespace smt { + class theory_pb : public theory { + + struct psort_expr; + class pb_justification; + class pb_model_value_proc; + class unwatch_ge; + class rewatch_vars; + class negate_ineq; + class remove_var; + class undo_bound; + typedef rational numeral; + typedef simplex::simplex simplex; + typedef simplex::row row; + typedef simplex::row_iterator row_iterator; + typedef unsynch_mpq_inf_manager eps_manager; + typedef _scoped_numeral scoped_eps_numeral; + + struct arg_t : public vector > { + numeral m_k; // invariants: m_k > 0, coeffs[i] > 0 + + unsigned get_hash() const; + bool operator==(arg_t const& other) const; + + numeral const& k() const { return m_k; } + + struct hash { + unsigned operator()(arg_t const& i) const { return i.get_hash(); } + }; + struct eq { + bool operator()(arg_t const& a, arg_t const& b) const { + return a == b; + } + }; + struct child_hash { + unsigned operator()(arg_t const& args, unsigned idx) const { + return args[idx].first.hash() ^ args[idx].second.hash(); + } + }; + struct kind_hash { + unsigned operator()(arg_t const& args) const { + return args.size(); + } + }; + + void remove_negations(); + + void negate(); + + lbool normalize(bool is_eq); + + void prune(bool is_eq); + + literal lit(unsigned i) const { + return (*this)[i].first; + } + + numeral const & coeff(unsigned i) const { return (*this)[i].second; } + + std::ostream& display(context& ctx, std::ostream& out, bool values = false) const; + + app_ref to_expr(bool is_eq, context& ctx, ast_manager& m); + + bool well_formed() const; + }; + + struct stats { + unsigned m_num_conflicts; + unsigned m_num_propagations; + unsigned m_num_predicates; + unsigned m_num_compiles; + unsigned m_num_compiled_vars; + unsigned m_num_compiled_clauses; + void reset() { memset(this, 0, sizeof(*this)); } + stats() { reset(); } + }; + + + struct ineq { + unsynch_mpz_manager& m_mpz; // mpz manager. + literal m_lit; // literal repesenting predicate + bool m_is_eq; // is this an = or >=. + arg_t m_args[2]; // encode args[0]*coeffs[0]+...+args[n-1]*coeffs[n-1] >= k(); + // Watch the first few positions until the sum satisfies: + // sum coeffs[i] >= m_lower + max_watch + scoped_mpz m_max_watch; // maximal coefficient. + unsigned m_watch_sz; // number of literals being watched. + scoped_mpz m_watch_sum; // maximal sum of watch literals. + // Watch infrastructure for = and unassigned >=: + unsigned m_nfixed; // number of variables that are fixed. + scoped_mpz m_max_sum; // maximal possible sum. + scoped_mpz m_min_sum; // minimal possible sum. + unsigned m_num_propagations; + unsigned m_compilation_threshold; + lbool m_compiled; + + ineq(unsynch_mpz_manager& m, literal l, bool is_eq) : + m_mpz(m), m_lit(l), m_is_eq(is_eq), + m_max_watch(m), m_watch_sum(m), + m_max_sum(m), m_min_sum(m) { + reset(); + } + + arg_t const& args() const { return m_args[m_lit.sign()]; } + arg_t& args() { return m_args[m_lit.sign()]; } + + literal lit() const { return m_lit; } + numeral const & k() const { return args().m_k; } + mpz const & mpz_k() const { return k().to_mpq().numerator(); } + + literal lit(unsigned i) const { return args()[i].first; } + numeral const & coeff(unsigned i) const { return args()[i].second; } + class mpz const& ncoeff(unsigned i) const { return coeff(i).to_mpq().numerator(); } + + unsigned size() const { return args().size(); } + + scoped_mpz const& watch_sum() const { return m_watch_sum; } + scoped_mpz const& max_watch() const { return m_max_watch; } + void set_max_watch(mpz const& n) { m_max_watch = n; } + unsigned watch_size() const { return m_watch_sz; } + + // variable watch infrastructure + scoped_mpz const& min_sum() const { return m_min_sum; } + scoped_mpz const& max_sum() const { return m_max_sum; } + unsigned nfixed() const { return m_nfixed; } + bool vwatch_initialized() const { return !m_mpz.is_zero(max_sum()); } + void vwatch_reset() { m_min_sum.reset(); m_max_sum.reset(); m_nfixed = 0; } + + unsigned find_lit(bool_var v, unsigned begin, unsigned end) { + while (lit(begin).var() != v) { + ++begin; + SASSERT(begin < end); + } + return begin; + } + + void reset(); + + void negate(); + + lbool normalize(); + + void unique(); + + void prune(); + + void post_prune(); + + app_ref to_expr(context& ctx, ast_manager& m); + + bool is_eq() const { return m_is_eq; } + bool is_ge() const { return !m_is_eq; } + + }; + + struct row_info { + unsigned m_slack; // slack variable in simplex tableau + numeral m_bound; // bound + arg_t m_rep; // representative + row_info(theory_var slack, numeral const& b, arg_t const& r): + m_slack(slack), m_bound(b), m_rep(r) {} + row_info(): m_slack(0) {} + }; + + typedef ptr_vector watch_list; + typedef map arg_map; + + theory_pb_params m_params; + u_map m_lwatch; // per literal. + u_map m_vwatch; // per variable. + u_map m_ineqs; // per inequality. + arg_map m_ineq_rep; // Simplex: representative inequality + u_map m_ineq_row_info; // Simplex: row information per variable + uint_set m_vars; // Simplex: 0-1 variables. + simplex m_simplex; // Simplex: tableau + literal_vector m_explain_lower; // Simplex: explanations for lower bounds + literal_vector m_explain_upper; // Simplex: explanations for upper bounds + unsynch_mpq_inf_manager m_mpq_inf_mgr; // Simplex: manage inf_mpq numerals + mutable unsynch_mpz_manager m_mpz_mgr; // Simplex: manager mpz numerals + unsigned_vector m_ineqs_trail; + unsigned_vector m_ineqs_lim; + literal_vector m_literals; // temporary vector + pb_util m_util; + stats m_stats; + ptr_vector m_to_compile; // inequalities to compile. + unsigned m_conflict_frequency; + bool m_learn_complements; + bool m_enable_compilation; + bool m_enable_simplex; + rational m_max_compiled_coeff; + + // internalize_atom: + literal compile_arg(expr* arg); + void add_watch(ineq& c, unsigned index); + void del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index); + void init_watch_literal(ineq& c); + void init_watch_var(ineq& c); + void clear_watch(ineq& c); + void watch_literal(literal lit, ineq* c); + void watch_var(bool_var v, ineq* c); + void unwatch_literal(literal w, ineq* c); + void unwatch_var(bool_var v, ineq* c); + void remove(ptr_vector& ineqs, ineq* c); + bool assign_watch_ge(bool_var v, bool is_true, watch_list& watch, unsigned index); + void assign_watch(bool_var v, bool is_true, ineq& c); + void assign_ineq(ineq& c, bool is_true); + void assign_eq(ineq& c, bool is_true); + + // simplex: + literal set_explain(literal_vector& explains, unsigned var, literal expl); + bool update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound); + bool check_feasible(); + + std::ostream& display(std::ostream& out, ineq const& c, bool values = false) const; + std::ostream& display(std::ostream& out, arg_t const& c, bool values = false) const; + virtual void display(std::ostream& out) const; + void display_resolved_lemma(std::ostream& out) const; + + void add_clause(ineq& c, literal_vector const& lits); + void add_assign(ineq& c, literal_vector const& lits, literal l); + literal_vector& get_lits(); + + literal_vector& get_all_literals(ineq& c, bool negate); + literal_vector& get_helpful_literals(ineq& c, bool negate); + literal_vector& get_unhelpful_literals(ineq& c, bool negate); + + // + // Utilities to compile cardinality + // constraints into a sorting network. + // + void compile_ineq(ineq& c); + void inc_propagations(ineq& c); + unsigned get_compilation_threshold(ineq& c); + + // + // Conflict resolution, cutting plane derivation. + // + unsigned m_num_marks; + unsigned m_conflict_lvl; + arg_t m_lemma; + literal_vector m_ineq_literals; + svector m_marked; + + // bool_var |-> index into m_lemma + unsigned_vector m_conseq_index; + static const unsigned null_index; + bool is_marked(bool_var v) const; + void set_mark(bool_var v, unsigned idx); + void unset_mark(bool_var v); + void unset_marks(); + + bool resolve_conflict(ineq& c); + void process_antecedent(literal l, numeral coeff); + void process_ineq(ineq& c, literal conseq, numeral coeff); + void remove_from_lemma(unsigned idx); + bool is_proof_justification(justification const& j) const; + + void hoist_maximal_values(); + + void validate_final_check(); + void validate_final_check(ineq& c); + void validate_assign(ineq const& c, literal_vector const& lits, literal l) const; + void validate_watch(ineq const& c) const; + + bool proofs_enabled() const { return get_manager().proofs_enabled(); } + justification* justify(literal l1, literal l2); + justification* justify(literal_vector const& lits); + + public: + theory_pb(ast_manager& m, theory_pb_params& p); + + virtual ~theory_pb(); + + virtual theory * mk_fresh(context * new_ctx); + virtual bool internalize_atom(app * atom, bool gate_ctx); + virtual bool internalize_term(app * term) { UNREACHABLE(); return false; } + virtual void new_eq_eh(theory_var v1, theory_var v2); + virtual void new_diseq_eh(theory_var v1, theory_var v2) { } + virtual bool use_diseqs() const { return false; } + virtual bool build_models() const { return false; } + virtual final_check_status final_check_eh(); + virtual void reset_eh(); + virtual void assign_eh(bool_var v, bool is_true); + virtual void init_search_eh(); + virtual void push_scope_eh(); + virtual void pop_scope_eh(unsigned num_scopes); + virtual void restart_eh(); + virtual void collect_statistics(::statistics & st) const; + virtual model_value_proc * mk_value(enode * n, model_generator & mg); + virtual void init_model(model_generator & m); + + static literal assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs); + }; +}; diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h index 4a5d97d52..5358fb758 100644 --- a/src/smt/theory_utvpi.h +++ b/src/smt/theory_utvpi.h @@ -138,8 +138,7 @@ namespace smt { smt_params m_params; arith_util a; arith_eq_adapter m_arith_eq_adapter; - th_var m_zero_int; // cache the variable representing the zero variable. - th_var m_zero_real; // cache the variable representing the zero variable. + th_var m_zero; //cache the variable representing the zero variable. dl_graph m_graph; nc_functor m_nc_functor; @@ -304,7 +303,7 @@ namespace smt { void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); - th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } + th_var get_zero(sort* s) const { return m_zero; } th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 6039b208a..e6ec7e387 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -62,8 +62,7 @@ namespace smt { theory(m.mk_family_id("arith")), a(m), m_arith_eq_adapter(*this, m_params, a), - m_zero_int(null_theory_var), - m_zero_real(null_theory_var), + m_zero(null_theory_var), m_nc_functor(*this), m_asserted_qhead(0), m_agility(0.5), @@ -134,8 +133,7 @@ namespace smt { template void theory_utvpi::reset_eh() { m_graph .reset(); - m_zero_int = null_theory_var; - m_zero_real = null_theory_var; + m_zero = null_theory_var; m_atoms .reset(); m_asserted_atoms .reset(); m_stats .reset(); @@ -261,8 +259,7 @@ namespace smt { template void theory_utvpi::init(context* ctx) { theory::init(ctx); - m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); - m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); + m_zero = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); } /** @@ -556,7 +553,7 @@ namespace smt { theory_var v = null_theory_var; context& ctx = get_context(); if (r.is_zero()) { - v = a.is_int(n)?m_zero_int:m_zero_real; + v = m_zero; } else if (ctx.e_internalized(n)) { enode* e = ctx.get_enode(n); @@ -778,9 +775,7 @@ namespace smt { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); enforce_parity(); - m_graph.set_to_zero(to_var(m_zero_int), to_var(m_zero_real)); - m_graph.set_to_zero(neg(to_var(m_zero_int)), neg(to_var(m_zero_real))); - m_graph.set_to_zero(to_var(m_zero_int), neg(to_var(m_zero_int))); + m_graph.set_to_zero(to_var(m_zero), neg(to_var(m_zero))); compute_delta(); DEBUG_CODE(validate_model();); } diff --git a/src/smt/theory_wmaxsat.cpp b/src/smt/theory_wmaxsat.cpp new file mode 100644 index 000000000..9ed944d2c --- /dev/null +++ b/src/smt/theory_wmaxsat.cpp @@ -0,0 +1,311 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_wmaxsat.h + +Abstract: + + Weighted Max-SAT theory plugin. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-05 + +Notes: + +--*/ + +#include +#include "smt_context.h" +#include "ast_pp.h" +#include "theory_wmaxsat.h" + +namespace smt { + +theory_wmaxsat::theory_wmaxsat(ast_manager& m, filter_model_converter& mc): + theory(m.mk_family_id("weighted_maxsat")), + m_mc(mc), + m_vars(m), + m_fmls(m), + m_zweights(m_mpz), + m_old_values(m_mpz), + m_zcost(m_mpz), + m_zmin_cost(m_mpz), + m_found_optimal(false), + m_propagate(false), + m_normalize(false) +{} + +theory_wmaxsat::~theory_wmaxsat() { + m_old_values.reset(); +} + +/** + \brief return the complement of variables that are currently assigned. +*/ +void theory_wmaxsat::get_assignment(svector& result) { + result.reset(); + + if (!m_found_optimal) { + for (unsigned i = 0; i < m_vars.size(); ++i) { + result.push_back(false); + } + } + else { + std::sort(m_cost_save.begin(), m_cost_save.end()); + for (unsigned i = 0,j = 0; i < m_vars.size(); ++i) { + if (j < m_cost_save.size() && m_cost_save[j] == static_cast(i)) { + result.push_back(false); + ++j; + } + else { + result.push_back(true); + } + } + } + TRACE("opt", + tout << "cost save: "; + for (unsigned i = 0; i < m_cost_save.size(); ++i) { + tout << m_cost_save[i] << " "; + } + tout << "\nvars: "; + for (unsigned i = 0; i < m_vars.size(); ++i) { + tout << mk_pp(m_vars[i].get(), get_manager()) << " "; + } + tout << "\nassignment: "; + for (unsigned i = 0; i < result.size(); ++i) { + tout << result[i] << " "; + } + tout << "\n";); + +} + +void theory_wmaxsat::init_search_eh() { + m_propagate = true; +} + +bool_var theory_wmaxsat::assert_weighted(expr* fml, rational const& w) { + context & ctx = get_context(); + ast_manager& m = get_manager(); + app_ref var(m), wfml(m); + var = m.mk_fresh_const("w", m.mk_bool_sort()); + m_mc.insert(var->get_decl()); + wfml = m.mk_or(var, fml); + ctx.assert_expr(wfml); + m_rweights.push_back(w); + m_vars.push_back(var); + m_fmls.push_back(fml); + m_assigned.push_back(false); + m_rmin_cost += w; + m_normalize = true; + return register_var(var, true); +} + +bool_var theory_wmaxsat::register_var(app* var, bool attach) { + context & ctx = get_context(); + bool_var bv; + SASSERT(!ctx.e_internalized(var)); + enode* x = ctx.mk_enode(var, false, true, true); + if (ctx.b_internalized(var)) { + bv = ctx.get_bool_var(var); + } + else { + bv = ctx.mk_bool_var(var); + } + ctx.set_enode_flag(bv, true); + if (attach) { + ctx.set_var_theory(bv, get_id()); + theory_var v = mk_var(x); + ctx.attach_th_var(x, this, v); + m_bool2var.insert(bv, v); + SASSERT(v == static_cast(m_var2bool.size())); + m_var2bool.push_back(bv); + SASSERT(ctx.bool_var2enode(bv)); + } + return bv; +} + +rational const& theory_wmaxsat::get_min_cost() { + unsynch_mpq_manager mgr; + scoped_mpq q(mgr); + mgr.set(q, m_zmin_cost, m_den.to_mpq().numerator()); + m_rmin_cost = rational(q); + return m_rmin_cost; +} + +void theory_wmaxsat::assign_eh(bool_var v, bool is_true) { + TRACE("opt", tout << "Assign " << mk_pp(m_vars[m_bool2var[v]].get(), get_manager()) << " " << is_true << "\n";); + if (is_true) { + if (m_normalize) normalize(); + context& ctx = get_context(); + theory_var tv = m_bool2var[v]; + if (m_assigned[tv]) return; + scoped_mpz w(m_mpz); + w = m_zweights[tv]; + ctx.push_trail(numeral_trail(m_zcost, m_old_values)); + ctx.push_trail(push_back_vector >(m_costs)); + ctx.push_trail(value_trail(m_assigned[tv])); + m_zcost += w; + m_costs.push_back(tv); + m_assigned[tv] = true; + if (m_zcost > m_zmin_cost) { + block(); + } + } +} + +final_check_status theory_wmaxsat::final_check_eh() { + if (m_normalize) normalize(); + return FC_DONE; +} + + +void theory_wmaxsat::reset_eh() { + theory::reset_eh(); + reset_local(); +} + +void theory_wmaxsat::reset_local() { + m_vars.reset(); + m_fmls.reset(); + m_rweights.reset(); + m_rmin_cost.reset(); + m_rcost.reset(); + m_zweights.reset(); + m_zcost.reset(); + m_zmin_cost.reset(); + m_cost_save.reset(); + m_bool2var.reset(); + m_var2bool.reset(); + m_propagate = false; + m_found_optimal = false; + m_assigned.reset(); +} + + +void theory_wmaxsat::propagate() { + context& ctx = get_context(); + for (unsigned i = 0; m_propagate && i < m_vars.size(); ++i) { + bool_var bv = m_var2bool[i]; + lbool asgn = ctx.get_assignment(bv); + if (asgn == l_true) { + assign_eh(bv, true); + } + } + m_propagate = false; +} + +bool theory_wmaxsat::is_optimal() const { + return !m_found_optimal || m_zcost < m_zmin_cost; +} + +expr_ref theory_wmaxsat::mk_block() { + ++m_stats.m_num_blocks; + ast_manager& m = get_manager(); + expr_ref_vector disj(m); + compare_cost compare_cost(*this); + svector costs(m_costs); + std::sort(costs.begin(), costs.end(), compare_cost); + scoped_mpz weight(m_mpz); + m_mpz.reset(weight); + for (unsigned i = 0; i < costs.size() && m_mpz.lt(weight, m_zmin_cost); ++i) { + weight += m_zweights[costs[i]]; + disj.push_back(m.mk_not(m_vars[costs[i]].get())); + } + if (is_optimal()) { + unsynch_mpq_manager mgr; + scoped_mpq q(mgr); + mgr.set(q, m_zmin_cost, m_den.to_mpq().numerator()); + rational rw = rational(q); + m_zmin_cost = weight; + m_found_optimal = true; + m_cost_save.reset(); + m_cost_save.append(m_costs); + TRACE("opt", + tout << "costs: "; + for (unsigned i = 0; i < m_costs.size(); ++i) { + tout << mk_pp(get_enode(m_costs[i])->get_owner(), get_manager()) << " "; + } + tout << "\n"; + get_context().display(tout); + ); + } + expr_ref result(m.mk_or(disj.size(), disj.c_ptr()), m); + TRACE("opt", + tout << result << " weight: " << weight << "\n"; + tout << "cost: " << m_zcost << " min-cost: " << m_zmin_cost << "\n";); + return result; +} + +expr_ref theory_wmaxsat::mk_optimal_block(svector const& ws, rational const& weight) { + ast_manager& m = get_manager(); + expr_ref_vector disj(m); + rational new_w = weight*m_den; + m_zmin_cost = new_w.to_mpq().numerator(); + m_cost_save.reset(); + for (unsigned i = 0; i < ws.size(); ++i) { + bool_var bv = ws[i]; + theory_var v = m_bool2var[bv]; + m_cost_save.push_back(v); + disj.push_back(m.mk_not(m_vars[v].get())); + } + expr_ref result(m.mk_or(disj.size(), disj.c_ptr()), m); + return result; +} + +void theory_wmaxsat::block() { + if (m_vars.empty()) { + return; + } + ++m_stats.m_num_blocks; + ast_manager& m = get_manager(); + context& ctx = get_context(); + literal_vector lits; + compare_cost compare_cost(*this); + svector costs(m_costs); + std::sort(costs.begin(), costs.end(), compare_cost); + + scoped_mpz weight(m_mpz); + m_mpz.reset(weight); + for (unsigned i = 0; i < costs.size() && weight < m_zmin_cost; ++i) { + weight += m_zweights[costs[i]]; + lits.push_back(~literal(m_var2bool[costs[i]])); + } + TRACE("opt", + tout << "block: "; + for (unsigned i = 0; i < lits.size(); ++i) { + expr_ref tmp(m); + ctx.literal2expr(lits[i], tmp); + tout << tmp << " "; + } + tout << "\n"; + ); + + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); +} + + +void theory_wmaxsat::normalize() { + m_den = rational::one(); + for (unsigned i = 0; i < m_rweights.size(); ++i) { + m_den = lcm(m_den, denominator(m_rweights[i])); + } + m_den = lcm(m_den, denominator(m_rmin_cost)); + SASSERT(!m_den.is_zero()); + m_zweights.reset(); + for (unsigned i = 0; i < m_rweights.size(); ++i) { + rational r = m_rweights[i]*m_den; + SASSERT(r.is_int()); + mpq const& q = r.to_mpq(); + m_zweights.push_back(q.numerator()); + } + rational r = m_rcost* m_den; + m_zcost = r.to_mpq().numerator(); + r = m_rmin_cost * m_den; + m_zmin_cost = r.to_mpq().numerator(); + m_normalize = false; +} + +}; diff --git a/src/smt/theory_wmaxsat.h b/src/smt/theory_wmaxsat.h new file mode 100644 index 000000000..1ddb388ca --- /dev/null +++ b/src/smt/theory_wmaxsat.h @@ -0,0 +1,128 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + theory_wmaxsat.h + +Abstract: + + Weighted Max-SAT theory plugin. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-05 + +Notes: + +--*/ + +#ifndef _THEORY_WMAXSAT_H_ +#define _THEORY_WMAXSAT_H_ + +#include "smt_theory.h" +#include "smt_clause.h" +#include "filter_model_converter.h" + +namespace smt { + class theory_wmaxsat : public theory { + struct stats { + unsigned m_num_blocks; + void reset() { memset(this, 0, sizeof(*this)); } + stats() { reset(); } + }; + filter_model_converter& m_mc; + mutable unsynch_mpz_manager m_mpz; + app_ref_vector m_vars; // Auxiliary variables per soft clause + expr_ref_vector m_fmls; // Formulas per soft clause + vector m_rweights; // weights of theory variables. + scoped_mpz_vector m_zweights; + scoped_mpz_vector m_old_values; + svector m_costs; // set of asserted theory variables + svector m_cost_save; // set of asserted theory variables + rational m_rcost; // current sum of asserted costs + rational m_rmin_cost; // current maximal cost assignment. + scoped_mpz m_zcost; // current sum of asserted costs + scoped_mpz m_zmin_cost; // current maximal cost assignment. + bool m_found_optimal; + u_map m_bool2var; // bool_var -> theory_var + svector m_var2bool; // theory_var -> bool_var + bool m_propagate; + bool m_normalize; + rational m_den; // lcm of denominators for rational weights. + svector m_assigned; + stats m_stats; + public: + theory_wmaxsat(ast_manager& m, filter_model_converter& mc); + virtual ~theory_wmaxsat(); + void get_assignment(svector& result); + virtual void init_search_eh(); + bool_var assert_weighted(expr* fml, rational const& w); + bool_var register_var(app* var, bool attach); + rational const& get_min_cost(); + class numeral_trail : public trail { + typedef scoped_mpz T; + T & m_value; + scoped_mpz_vector& m_old_values; + public: + numeral_trail(T & value, scoped_mpz_vector& old): + m_value(value), + m_old_values(old) { + old.push_back(value); + } + + virtual ~numeral_trail() { + } + + virtual void undo(context & ctx) { + m_value = m_old_values.back(); + m_old_values.shrink(m_old_values.size() - 1); + } + }; + virtual void assign_eh(bool_var v, bool is_true); + virtual final_check_status final_check_eh(); + virtual bool use_diseqs() const { + return false; + } + virtual bool build_models() const { + return false; + } + void reset_local(); + virtual void reset_eh(); + virtual theory * mk_fresh(context * new_ctx) { return 0; } + virtual bool internalize_atom(app * atom, bool gate_ctx) { return false; } + virtual bool internalize_term(app * term) { return false; } + virtual void new_eq_eh(theory_var v1, theory_var v2) { } + virtual void new_diseq_eh(theory_var v1, theory_var v2) { } + + virtual void collect_statistics(::statistics & st) const { + st.update("wmaxsat num blocks", m_stats.m_num_blocks); + } + virtual bool can_propagate() { + return m_propagate; + } + + virtual void propagate(); + bool is_optimal() const; + expr_ref mk_block(); + + expr_ref mk_optimal_block(svector const& ws, rational const& weight); + private: + + void block(); + void normalize(); + + class compare_cost { + theory_wmaxsat& m_th; + public: + compare_cost(theory_wmaxsat& t):m_th(t) {} + bool operator() (theory_var v, theory_var w) const { + return m_th.m_mpz.gt(m_th.m_zweights[v], m_th.m_zweights[w]); + } + }; + + + }; +}; + +#endif diff --git a/src/smt/watch_list.cpp b/src/smt/watch_list.cpp index 731e021fe..ed73b9ed5 100644 --- a/src/smt/watch_list.cpp +++ b/src/smt/watch_list.cpp @@ -48,12 +48,17 @@ namespace smt { *mem = DEFAULT_WATCH_LIST_SIZE; ++mem; m_data = reinterpret_cast(mem); + SASSERT( begin_lits_core() % sizeof(literal) == 0 ); } else { unsigned curr_begin_bin = begin_lits_core(); unsigned curr_capacity = end_lits_core(); unsigned bin_bytes = curr_capacity - curr_begin_bin; - unsigned new_capacity = (curr_capacity * 3 + sizeof(clause *)) >> 1; + /* dvitek: Added +3&~3U to fix alignment issues on + * sparc64/solaris. ("literal"s must be 4-byte aligned). Should + * also help performance elsewhere. + */ + unsigned new_capacity = (((curr_capacity * 3 + sizeof(clause *)) >> 1)+3)&~3U; unsigned * mem = reinterpret_cast(alloc_svect(char, new_capacity + HEADER_SIZE)); unsigned curr_end_cls = end_cls_core(); #ifdef _AMD64_ @@ -71,6 +76,7 @@ namespace smt { memcpy(reinterpret_cast(mem) + new_begin_bin, m_data + curr_begin_bin, bin_bytes); destroy(); m_data = reinterpret_cast(mem); + SASSERT( begin_lits_core() % sizeof(literal) == 0 ); } } diff --git a/src/solver/solver.h b/src/solver/solver.h index b5a407e2a..a1f199e61 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -139,6 +139,14 @@ public: \brief Display the content of this solver. */ virtual void display(std::ostream & out) const; + class scoped_push { + solver& s; + bool m_nopop; + public: + scoped_push(solver& s):s(s), m_nopop(false) { s.push(); } + ~scoped_push() { if (!m_nopop) s.pop(1); } + void disable_pop() { m_nopop = true; } + }; protected: virtual void set_cancel(bool f) = 0; diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index fb4898ecd..375f35ed1 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -40,6 +40,7 @@ class tactic2solver : public solver_na2as { bool m_produce_models; bool m_produce_proofs; bool m_produce_unsat_cores; + statistics m_stats; public: tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic); virtual ~tactic2solver(); @@ -161,6 +162,7 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass m_result->m_unknown = ex.msg(); } m_tactic->collect_statistics(m_result->m_stats); + m_tactic->collect_statistics(m_stats); m_result->m_model = md; m_result->m_proof = pr; if (m_produce_unsat_cores) { @@ -181,9 +183,9 @@ void tactic2solver::set_cancel(bool f) { } } -void tactic2solver::collect_statistics(statistics & st) const { - if (m_result.get()) - m_result->collect_statistics(st); +void tactic2solver::collect_statistics(statistics & st) const { + st.copy(m_stats); + //SASSERT(m_stats.size() > 0); } void tactic2solver::get_unsat_core(ptr_vector & r) { diff --git a/src/tactic/aig/aig_tactic.cpp b/src/tactic/aig/aig_tactic.cpp index dc577eb73..f1f1c7330 100644 --- a/src/tactic/aig/aig_tactic.cpp +++ b/src/tactic/aig/aig_tactic.cpp @@ -78,13 +78,13 @@ public: mk_aig_manager mk(*this, g->m()); if (m_aig_per_assertion) { - unsigned size = g->size(); - for (unsigned i = 0; i < size; i++) { + for (unsigned i = 0; i < g->size(); i++) { aig_ref r = m_aig_manager->mk_aig(g->form(i)); m_aig_manager->max_sharing(r); expr_ref new_f(g->m()); m_aig_manager->to_formula(r, new_f); - g->update(i, new_f, 0, g->dep(i)); + expr_dependency * ed = g->dep(i); + g->update(i, new_f, 0, ed); } } else { diff --git a/src/tactic/arith/arith_bounds_tactic.cpp b/src/tactic/arith/arith_bounds_tactic.cpp index 0de507183..83b5c6daf 100644 --- a/src/tactic/arith/arith_bounds_tactic.cpp +++ b/src/tactic/arith/arith_bounds_tactic.cpp @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #include"arith_bounds_tactic.h" #include"arith_decl_plugin.h" @@ -34,8 +40,8 @@ struct arith_bounds_tactic : public tactic { bounds_arith_subsumption(in, result); } - virtual tactic* translate(ast_manager& m) { - return alloc(arith_bounds_tactic, m); + virtual tactic* translate(ast_manager & mgr) { + return alloc(arith_bounds_tactic, mgr); } void checkpoint() { diff --git a/src/tactic/arith/card2bv_tactic.cpp b/src/tactic/arith/card2bv_tactic.cpp new file mode 100644 index 000000000..465173196 --- /dev/null +++ b/src/tactic/arith/card2bv_tactic.cpp @@ -0,0 +1,518 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + card2bv_tactic.cpp + +Abstract: + + Tactic for converting Pseudo-Boolean constraints to BV + +Author: + + Nikolaj Bjorner (nbjorner) 2014-03-20 + +Notes: + +--*/ +#include"tactical.h" +#include"cooperate.h" +#include"rewriter_def.h" +#include"ast_smt2_pp.h" +#include"expr_substitution.h" +#include"card2bv_tactic.h" +#include"pb_rewriter.h" +#include"ast_util.h" + +namespace pb { + unsigned card2bv_rewriter::get_num_bits(func_decl* f) { + rational r(0); + unsigned sz = f->get_arity(); + for (unsigned i = 0; i < sz; ++i) { + r += pb.get_coeff(f, i); + } + r = r > pb.get_k(f)? r : pb.get_k(f); + return r.get_num_bits(); + } + + card2bv_rewriter::card2bv_rewriter(ast_manager& m): + m(m), + au(m), + pb(m), + bv(m), + m_sort(*this), + m_lemmas(m), + m_trail(m) + {} + + void card2bv_rewriter::mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas) { + m_lemmas.reset(); + SASSERT(f->get_family_id() == pb.get_family_id()); + if (is_or(f)) { + result = m.mk_or(sz, args); + } + else if (is_and(f)) { + result = m.mk_and(sz, args); + } + else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + result = m_sort.eq(pb.get_k(f).get_unsigned(), sz, args); + } + else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + result = m_sort.le(false, pb.get_k(f).get_unsigned(), sz, args); + } + else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + result = m_sort.ge(false, pb.get_k(f).get_unsigned(), sz, args); + } + else { + br_status st = mk_shannon(f, sz, args, result); + if (st == BR_FAILED) { + mk_bv(f, sz, args, result); + } + } + lemmas.append(m_lemmas); + } + + std::ostream& card2bv_rewriter::pp(std::ostream& out, literal lit) { + return out << mk_ismt2_pp(lit, m); + } + + card2bv_rewriter::literal card2bv_rewriter::trail(literal l) { + m_trail.push_back(l); + return l; + } + card2bv_rewriter::literal card2bv_rewriter::fresh() { + return trail(m.mk_fresh_const("sn", m.mk_bool_sort())); + } + + void card2bv_rewriter::mk_clause(unsigned n, literal const* lits) { + m_lemmas.push_back(mk_or(m, n, lits)); + } + + + br_status card2bv_rewriter::mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { + if (f->get_family_id() == null_family_id) { + if (sz == 1) { + // Expecting minimize/maximize. + func_decl_ref fd(m); + fd = m.mk_func_decl(f->get_name(), m.get_sort(args[0]), f->get_range()); + result = m.mk_app(fd.get(), args[0]); + return BR_DONE; + } + else + return BR_FAILED; + } + else if (f->get_family_id() == m.get_basic_family_id()) { + result = m.mk_app(f, sz, args); + return BR_DONE; + } + else if (f->get_family_id() == pb.get_family_id()) { + if (is_or(f)) { + result = m.mk_or(sz, args); + return BR_DONE; + } + if (is_and(f)) { + result = m.mk_and(sz, args); + return BR_DONE; + } + br_status st = mk_shannon(f, sz, args, result); + if (st == BR_FAILED) { + mk_bv(f, sz, args, result); + return BR_DONE; + } + else { + return st; + } + } + // NSB: review + // we should remove this code and rely on a layer above to deal with + // whatever it accomplishes. It seems to break types. + // + else if (f->get_family_id() == au.get_family_id()) { + if (f->get_decl_kind() == OP_ADD) { + unsigned bits = 0; + for (unsigned i = 0; i < sz; i++) { + rational val1, val2; + if (au.is_int(args[i]) && au.is_numeral(args[i], val1)) { + bits += val1.get_num_bits(); + } + else if (m.is_ite(args[i]) && + au.is_numeral(to_app(args[i])->get_arg(1), val1) && val1.is_one() && + au.is_numeral(to_app(args[i])->get_arg(2), val2) && val2.is_zero()) { + bits++; + } + else + return BR_FAILED; + } + + result = 0; + for (unsigned i = 0; i < sz; i++) { + rational val1, val2; + expr * q; + if (au.is_int(args[i]) && au.is_numeral(args[i], val1)) + q = bv.mk_numeral(val1, bits); + else + q = mk_ite(to_app(args[i])->get_arg(0), bv.mk_numeral(1, bits), bv.mk_numeral(0, bits)); + result = (i == 0) ? q : bv.mk_bv_add(result.get(), q); + } + return BR_DONE; + } + else + return BR_FAILED; + } + else + return BR_FAILED; + } + + bool card2bv_rewriter::is_or(func_decl* f) { + switch (f->get_decl_kind()) { + case OP_AT_MOST_K: + case OP_PB_LE: + return false; + case OP_AT_LEAST_K: + case OP_PB_GE: + return pb.get_k(f).is_one(); + case OP_PB_EQ: + return false; + default: + UNREACHABLE(); + return false; + } + } + + bool card2bv_rewriter::is_and(func_decl* f) { + return false; + } + + void card2bv_rewriter::mk_bv(func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { + expr_ref zero(m), a(m), b(m); + expr_ref_vector es(m); + unsigned bw = get_num_bits(f); + zero = bv.mk_numeral(rational(0), bw); + for (unsigned i = 0; i < sz; ++i) { + es.push_back(mk_ite(args[i], bv.mk_numeral(pb.get_coeff(f, i), bw), zero)); + } + switch (es.size()) { + case 0: a = zero; break; + case 1: a = es[0].get(); break; + default: + a = es[0].get(); + for (unsigned i = 1; i < es.size(); ++i) { + a = bv.mk_bv_add(a, es[i].get()); + } + break; + } + b = bv.mk_numeral(pb.get_k(f), bw); + + switch (f->get_decl_kind()) { + case OP_AT_MOST_K: + case OP_PB_LE: + UNREACHABLE(); + result = bv.mk_ule(a, b); + break; + case OP_AT_LEAST_K: + UNREACHABLE(); + case OP_PB_GE: + result = bv.mk_ule(b, a); + break; + case OP_PB_EQ: + result = m.mk_eq(a, b); + break; + default: + UNREACHABLE(); + } + TRACE("card2bv", tout << result << "\n";); + } + + struct argc_t { + expr* m_arg; + rational m_coeff; + argc_t():m_arg(0), m_coeff(0) {} + argc_t(expr* arg, rational const& r): m_arg(arg), m_coeff(r) {} + }; + struct argc_gt { + bool operator()(argc_t const& a, argc_t const& b) const { + return a.m_coeff > b.m_coeff; + } + }; + struct argc_entry { + unsigned m_index; + rational m_k; + expr* m_value; + argc_entry(unsigned i, rational const& k): m_index(i), m_k(k), m_value(0) {} + argc_entry():m_index(0), m_k(0), m_value(0) {} + + struct eq { + bool operator()(argc_entry const& a, argc_entry const& b) const { + return a.m_index == b.m_index && a.m_k == b.m_k; + } + }; + struct hash { + unsigned operator()(argc_entry const& a) const { + return a.m_index ^ a.m_k.hash(); + } + }; + }; + typedef hashtable argc_cache; + + br_status card2bv_rewriter::mk_shannon( + func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { + + unsigned max_clauses = sz*10; + vector argcs; + for (unsigned i = 0; i < sz; ++i) { + argcs.push_back(argc_t(args[i], pb.get_coeff(f, i))); + } + std::sort(argcs.begin(), argcs.end(), argc_gt()); + DEBUG_CODE( + for (unsigned i = 0; i + 1 < sz; ++i) { + SASSERT(argcs[i].m_coeff >= argcs[i+1].m_coeff); + } + ); + result = m.mk_app(f, sz, args); + TRACE("card2bv", tout << result << "\n";); + argc_cache cache; + expr_ref_vector trail(m); + vector todo_k; + unsigned_vector todo_i; + todo_k.push_back(pb.get_k(f)); + todo_i.push_back(0); + decl_kind kind = f->get_decl_kind(); + argc_entry entry1; + while (!todo_i.empty()) { + SASSERT(todo_i.size() == todo_k.size()); + if (cache.size() > max_clauses) { + return BR_FAILED; + } + unsigned i = todo_i.back(); + rational k = todo_k.back(); + argc_entry entry(i, k); + if (cache.contains(entry)) { + todo_i.pop_back(); + todo_k.pop_back(); + continue; + } + SASSERT(i < sz); + SASSERT(!k.is_neg()); + rational const& coeff = argcs[i].m_coeff; + expr* arg = argcs[i].m_arg; + if (i + 1 == sz) { + switch(kind) { + case OP_AT_MOST_K: + case OP_PB_LE: + if (coeff <= k) { + entry.m_value = m.mk_true(); + } + else { + entry.m_value = negate(arg); + trail.push_back(entry.m_value); + } + break; + case OP_AT_LEAST_K: + case OP_PB_GE: + if (k.is_zero()) { + entry.m_value = m.mk_true(); + } + else if (coeff < k) { + entry.m_value = m.mk_false(); + } + else if (coeff.is_zero()) { + entry.m_value = m.mk_true(); + } + else { + SASSERT(coeff >= k && k.is_pos()); + entry.m_value = arg; + } + break; + case OP_PB_EQ: + if (coeff == k) { + entry.m_value = arg; + } + else if (k.is_zero()) { + entry.m_value = negate(arg); + trail.push_back(entry.m_value); + } + else { + entry.m_value = m.mk_false(); + } + break; + } + todo_i.pop_back(); + todo_k.pop_back(); + cache.insert(entry); + continue; + } + entry.m_index++; + expr* lo = 0, *hi = 0; + if (cache.find(entry, entry1)) { + lo = entry1.m_value; + } + else { + todo_i.push_back(i+1); + todo_k.push_back(k); + } + entry.m_k -= coeff; + if (kind != OP_PB_EQ && !entry.m_k.is_pos()) { + switch (kind) { + case OP_AT_MOST_K: + case OP_PB_LE: + hi = m.mk_false(); + break; + case OP_AT_LEAST_K: + case OP_PB_GE: + hi = m.mk_true(); + break; + default: + UNREACHABLE(); + } + } + else if (cache.find(entry, entry1)) { + hi = entry1.m_value; + } + else { + todo_i.push_back(i+1); + todo_k.push_back(entry.m_k); + } + if (hi && lo) { + todo_i.pop_back(); + todo_k.pop_back(); + entry.m_index = i; + entry.m_k = k; + entry.m_value = mk_ite(arg, hi, lo); + trail.push_back(entry.m_value); + cache.insert(entry); + } + } + argc_entry entry(0, pb.get_k(f)); + VERIFY(cache.find(entry, entry)); + result = entry.m_value; + TRACE("card2bv", tout << result << "\n";); + return BR_DONE; + } + + expr* card2bv_rewriter::negate(expr* e) { + if (m.is_not(e, e)) return e; + return m.mk_not(e); + } + + expr* card2bv_rewriter::mk_ite(expr* c, expr* hi, expr* lo) { + while (m.is_not(c, c)) { + std::swap(hi, lo); + } + if (hi == lo) return hi; + if (m.is_true(hi) && m.is_false(lo)) return c; + if (m.is_false(hi) && m.is_true(lo)) return negate(c); + if (m.is_true(hi)) return m.mk_or(c, lo); + if (m.is_false(lo)) return m.mk_and(c, hi); + if (m.is_false(hi)) return m.mk_and(negate(c), lo); + if (m.is_true(lo)) return m.mk_implies(c, hi); + return m.mk_ite(c, hi, lo); + } + + void card_pb_rewriter::rewrite(expr* e, expr_ref& result) { + if (pb.is_eq(e)) { + app* a = to_app(e); + ast_manager& m = m_lemmas.get_manager(); + unsigned sz = a->get_num_args(); + expr_ref_vector args(m); + expr_ref tmp(m); + for (unsigned i = 0; i < sz; ++i) { + (*this)(a->get_arg(i), tmp); + args.push_back(tmp); + } + m_cfg.m_r.mk_assert(a->get_decl(), sz, args.c_ptr(), result, m_lemmas); + } + else { + (*this)(e, result); + } + } + +}; + +template class rewriter_tpl; + + +class card2bv_tactic : public tactic { + ast_manager & m; + params_ref m_params; + th_rewriter m_rw1; + pb::card_pb_rewriter m_rw2; + +public: + + card2bv_tactic(ast_manager & m, params_ref const & p): + m(m), + m_params(p), + m_rw1(m), + m_rw2(m) { + } + + virtual tactic * translate(ast_manager & m) { + return alloc(card2bv_tactic, m, m_params); + } + + virtual ~card2bv_tactic() { + } + + virtual void updt_params(params_ref const & p) { + m_params = p; + } + + virtual void collect_param_descrs(param_descrs & r) { + } + + void set_cancel(bool f) { + m_rw1.set_cancel(f); + m_rw2.set_cancel(f); + } + + virtual void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + TRACE("card2bv-before", g->display(tout);); + SASSERT(g->is_well_sorted()); + fail_if_proof_generation("card2bv", g); + mc = 0; pc = 0; core = 0; result.reset(); + tactic_report report("card2bv", *g); + m_rw1.reset(); + m_rw2.reset(); + m_rw2.lemmas().reset(); + + if (g->inconsistent()) { + result.push_back(g.get()); + return; + } + + expr_ref new_f1(m), new_f2(m); + proof_ref new_pr1(m), new_pr2(m); + for (unsigned idx = 0; !g->inconsistent() && idx < g->size(); idx++) { + m_rw1(g->form(idx), new_f1, new_pr1); + TRACE("card2bv", tout << "Rewriting " << mk_ismt2_pp(new_f1.get(), m) << std::endl;); + m_rw2.rewrite(new_f1, new_f2); + if (m.proofs_enabled()) { + new_pr1 = m.mk_modus_ponens(g->pr(idx), new_pr1); + new_pr2 = m.mk_rewrite(new_f1, new_f2); + new_pr1 = m.mk_modus_ponens(new_pr1, new_pr2); + } + g->update(idx, new_f2, new_pr1, g->dep(idx)); + } + for (unsigned i = 0; i < m_rw2.lemmas().size(); ++i) { + g->assert_expr(m_rw2.lemmas()[i].get()); + } + + g->inc_depth(); + result.push_back(g.get()); + TRACE("card2bv", g->display(tout);); + SASSERT(g->is_well_sorted()); + } + + virtual void cleanup() { + } +}; + +tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(card2bv_tactic, m, p)); +} + diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h new file mode 100644 index 000000000..ed96376b5 --- /dev/null +++ b/src/tactic/arith/card2bv_tactic.h @@ -0,0 +1,108 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + card2bv_tactic.cpp + +Abstract: + + Tactic for converting Pseudo-Boolean constraints to BV + +Author: + + Nikolaj Bjorner (nbjorner) 2014-03-20 + +Notes: + +--*/ +#ifndef _CARD2BV_TACTIC_ +#define _CARD2BV_TACTIC_ + +#include"params.h" +#include"pb_decl_plugin.h" +#include"th_rewriter.h" +#include"rewriter.h" +#include +#include"sorting_network.h" + + +class ast_manager; +class tactic; + +namespace pb { + + class card2bv_rewriter { + public: + typedef expr* literal; + typedef ptr_vector literal_vector; + private: + ast_manager& m; + arith_util au; + pb_util pb; + bv_util bv; + psort_nw m_sort; + expr_ref_vector m_lemmas; + expr_ref_vector m_trail; + + unsigned get_num_bits(func_decl* f); + void mk_bv(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); + br_status mk_shannon(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); + expr* negate(expr* e); + expr* mk_ite(expr* c, expr* hi, expr* lo); + bool is_or(func_decl* f); + bool is_and(func_decl* f); + + public: + card2bv_rewriter(ast_manager& m); + br_status mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result); + void mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas); + + // definitions used for sorting network + literal mk_false() { return m.mk_false(); } + literal mk_true() { return m.mk_true(); } + literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } + literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } + literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + std::ostream& pp(std::ostream& out, literal lit); + literal fresh(); + literal trail(literal l); + void mk_clause(unsigned n, literal const* lits); + + }; + + struct card2bv_rewriter_cfg : public default_rewriter_cfg { + card2bv_rewriter m_r; + bool rewrite_patterns() const { return false; } + bool flat_assoc(func_decl * f) const { return false; } + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + result_pr = 0; + return m_r.mk_app_core(f, num, args, result); + } + card2bv_rewriter_cfg(ast_manager & m):m_r(m) {} + }; + + class card_pb_rewriter : public rewriter_tpl { + card2bv_rewriter_cfg m_cfg; + pb_util pb; + expr_ref_vector m_lemmas; + public: + card_pb_rewriter(ast_manager & m): + rewriter_tpl(m, false, m_cfg), + m_cfg(m), + pb(m), + m_lemmas(m) {} + + void rewrite(expr* e, expr_ref& result); + + expr_ref_vector& lemmas() { return m_lemmas; } + }; +}; + +tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p = params_ref()); +/* + ADD_TACTIC("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_card2bv_tactic(m, p)") +*/ + + +#endif diff --git a/src/tactic/arith/elim01_tactic.cpp b/src/tactic/arith/elim01_tactic.cpp new file mode 100644 index 000000000..dda3d8fc9 --- /dev/null +++ b/src/tactic/arith/elim01_tactic.cpp @@ -0,0 +1,270 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + elim01_tactic.cpp + +Abstract: + + Replace 0-1 integer variables by Booleans. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-12-7 + +Notes: + +--*/ +#include"tactical.h" +#include"cooperate.h" +#include"bound_manager.h" +#include"ast_pp.h" +#include"expr_safe_replace.h" // NB: should use proof-producing expr_substitute in polished version. +#include"arith_decl_plugin.h" +#include"elim01_tactic.h" +#include"model_smt2_pp.h" +#include"th_rewriter.h" + +class bool2int_model_converter : public model_converter { + ast_manager& m; + arith_util a; + func_decl_ref_vector m_refs; + obj_hashtable m_bools; + vector > m_nums_as_bool; + ptr_vector m_nums_as_int; +public: + + bool2int_model_converter(ast_manager& m): + m(m), + a(m), + m_refs(m) + {} + + virtual void operator()(model_ref & old_model, unsigned goal_idx) { + SASSERT(goal_idx == 0); + model * new_model = alloc(model, m); + unsigned num = old_model->get_num_constants(); + for (unsigned i = 0; i < m_nums_as_int.size(); ++i) { + func_decl* f_old = m_nums_as_int[i]; + rational val(0); + rational po(1); + bool is_value = true; + for (unsigned j = 0; is_value && j < m_nums_as_bool[i].size(); ++j) { + func_decl* f = m_nums_as_bool[i][j]; + expr* fi = old_model->get_const_interp(f); + if (!fi) { + is_value = false; + } + else if (m.is_true(fi)) { + val += po; + } + else if (!m.is_false(fi)) { + is_value = false; + } + po *= rational(2); + } + if (is_value) { + expr* fi = a.mk_numeral(val, true); + new_model->register_decl(f_old, fi); + } + } + for (unsigned i = 0; i < num; ++i) { + func_decl* f = old_model->get_constant(i); + expr* fi = old_model->get_const_interp(f); + if (!m_bools.contains(f)) { + new_model->register_decl(f, fi); + } + } + num = old_model->get_num_functions(); + for (unsigned i = 0; i < num; i++) { + func_decl * f = old_model->get_function(i); + func_interp * fi = old_model->get_func_interp(f); + new_model->register_decl(f, fi->copy()); + } + new_model->copy_usort_interps(*old_model); + old_model = new_model; + } + + void insert(func_decl* x_new, func_decl* x_old) { + m_refs.push_back(x_new); + m_refs.push_back(x_old); + m_bools.insert(x_new); + m_nums_as_int.push_back(x_old); + m_nums_as_bool.push_back(ptr_vector()); + m_nums_as_bool.back().push_back(x_new); + } + + void insert(func_decl* x_old, unsigned sz, func_decl * const* x_new) { + m_nums_as_int.push_back(x_old); + m_nums_as_bool.push_back(ptr_vector()); + m_refs.push_back(x_old); + for (unsigned i = 0; i < sz; ++i) { + m_refs.push_back(x_new[i]); + m_nums_as_bool.back().push_back(x_new[i]); + m_bools.insert(x_new[i]); + } + } + + virtual model_converter * translate(ast_translation & translator) { + bool2int_model_converter* mc = alloc(bool2int_model_converter, translator.to()); + for (unsigned i = 0; i < m_nums_as_int.size(); ++i) { + mc->insert(m_nums_as_int[i], m_nums_as_bool[i].size(), m_nums_as_bool[i].c_ptr()); + } + return mc; + } +}; + + +class elim01_tactic : public tactic { +public: + typedef obj_hashtable expr_set; + ast_manager & m; + arith_util a; + th_rewriter m_rewriter; + params_ref m_params; + unsigned m_max_hi_default; + rational m_max_hi; + + elim01_tactic(ast_manager & _m, params_ref const & p): + m(_m), + a(m), + m_rewriter(m), + m_max_hi_default(8), + m_max_hi(rational(m_max_hi_default)) { + } + + virtual ~elim01_tactic() { + } + + void set_cancel(bool f) { + } + + virtual void updt_params(params_ref const & p) { + m_max_hi = rational(p.get_uint("max_coefficient", m_max_hi_default)); + m_params = p; + } + + virtual void collect_param_descrs(param_descrs & r) { + r.insert("max_coefficient", CPK_UINT, "(default: 1) maximal upper bound for finite range -> Bool conversion"); + } + + + virtual void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + SASSERT(g->is_well_sorted()); + mc = 0; pc = 0; core = 0; + + tactic_report report("elim01", *g); + + expr_safe_replace sub(m); + bool2int_model_converter* b2i = alloc(bool2int_model_converter, m); + mc = b2i; + bound_manager bounds(m); + expr_ref_vector axioms(m); + bounds(*g); + + rational zero(0); + bound_manager::iterator bit = bounds.begin(), bend = bounds.end(); + for (; bit != bend; ++bit) { + if (!is_app(*bit)) continue; + app* x = to_app(*bit); + bool s1, s2; + rational lo, hi; + if (a.is_int(x) && + bounds.has_lower(x, lo, s1) && !s1 && zero <= lo && + bounds.has_upper(x, hi, s2) && !s2 && hi <= m_max_hi && lo <= hi) { + add_variable(b2i, sub, x, lo.get_unsigned(), hi.get_unsigned(), axioms); + } + else if (a.is_int(x)) { + TRACE("pb", tout << "Not adding variable " << mk_pp(x, m) << " has lower: " + << bounds.has_lower(x, lo, s1) << " " << lo << " has upper: " + << bounds.has_upper(x, hi, s2) << " " << hi << "\n";); + } + } + + if (sub.empty()) { + result.push_back(g.get()); + return; + } + + expr_ref new_curr(m), tmp_curr(m); + proof_ref new_pr(m); + for (unsigned i = 0; i < g->size(); i++) { + expr * curr = g->form(i); + sub(curr, tmp_curr); + m_rewriter(tmp_curr, new_curr); + if (m.proofs_enabled()) { + new_pr = m.mk_rewrite(curr, new_curr); + new_pr = m.mk_modus_ponens(g->pr(i), new_pr); + } + g->update(i, new_curr, new_pr, g->dep(i)); + } + for (unsigned i = 0; i < axioms.size(); ++i) { + g->assert_expr(axioms[i].get()); + } + g->inc_depth(); + result.push_back(g.get()); + TRACE("pb", g->display(tout);); + SASSERT(g->is_well_sorted()); + + // TBD: support proof conversion (or not..) + } + + virtual tactic * translate(ast_manager & m) { + return alloc(elim01_tactic, m, m_params); + } + + virtual void cleanup() {} + + void add_variable(bool2int_model_converter* b2i, + expr_safe_replace& sub, + app* x, + unsigned min_value, + unsigned max_value, + expr_ref_vector& axioms) { + std::string name = x->get_decl()->get_name().str(); + unsigned sh = 0; + app_ref_vector xs(m), ites(m); + func_decl_ref_vector xfs(m); + app_ref zero(m), sum(m); + zero = a.mk_numeral(rational(0), true); + while (max_value >= (1ul << sh)) { + xs.push_back(m.mk_fresh_const(name.c_str(), m.mk_bool_sort())); + xfs.push_back(xs.back()->get_decl()); + ites.push_back(m.mk_ite(xs.back(), a.mk_numeral(rational(1 << sh), true), zero)); + ++sh; + } + switch (ites.size()) { + case 0: + sum = zero; + break; + case 1: + sum = ites[0].get(); + break; + default: + sum = a.mk_add(ites.size(), (expr*const*)ites.c_ptr()); + break; + } + TRACE("pb", tout << mk_pp(x, m) << " " << sum << " max: " << max_value << "\n";); + + sub.insert(x, sum); + b2i->insert(x->get_decl(), xfs.size(), xfs.c_ptr()); + // if max_value+1 is not a power of two: + if ((max_value & (max_value + 1)) != 0) { + axioms.push_back(a.mk_le(sum, a.mk_numeral(rational(max_value), true))); + } + if (min_value > 0) { + axioms.push_back(a.mk_ge(sum, a.mk_numeral(rational(min_value), true))); + } + } + +}; + +tactic * mk_elim01_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(elim01_tactic, m, p)); +} + diff --git a/src/tactic/arith/elim01_tactic.h b/src/tactic/arith/elim01_tactic.h new file mode 100644 index 000000000..867013ed0 --- /dev/null +++ b/src/tactic/arith/elim01_tactic.h @@ -0,0 +1,33 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + elim01_tactic.h + +Abstract: + + Replace 0-1 integer variables by Booleans. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-12-7 + +Notes: + +--*/ +#ifndef _ELIM01_TACTIC_H_ +#define _ELIM01_TACTIC_H_ + +#include"params.h" +class ast_manager; +class tactic; + +tactic * mk_elim01_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* + ADD_TACTIC("elim01", "eliminate 0-1 integer variables, replace them by Booleans.", "mk_elim01_tactic(m, p)") +*/ + + +#endif diff --git a/src/tactic/arith/lia2card_tactic.cpp b/src/tactic/arith/lia2card_tactic.cpp new file mode 100644 index 000000000..7d9efd44b --- /dev/null +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -0,0 +1,311 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + lia2card_tactic.cpp + +Abstract: + + Convert 0-1 integer variables cardinality constraints to built-in cardinality operator. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-5 + +Notes: + +--*/ +#include"tactical.h" +#include"cooperate.h" +#include"bound_manager.h" +#include"ast_pp.h" +#include"expr_safe_replace.h" // NB: should use proof-producing expr_substitute in polished version. +#include"pb_decl_plugin.h" +#include"arith_decl_plugin.h" + +class lia2card_tactic : public tactic { +public: + typedef obj_hashtable expr_set; + ast_manager & m; + arith_util a; + params_ref m_params; + pb_util m_pb; + mutable ptr_vector* m_todo; + expr_set* m_01s; + bool m_compile_equality; + + lia2card_tactic(ast_manager & _m, params_ref const & p): + m(_m), + a(m), + m_pb(m), + m_todo(alloc(ptr_vector)), + m_01s(alloc(expr_set)), + m_compile_equality(false) { + } + + virtual ~lia2card_tactic() { + dealloc(m_todo); + dealloc(m_01s); + } + + void set_cancel(bool f) { + } + + void updt_params(params_ref const & p) { + m_params = p; + m_compile_equality = p.get_bool("compile_equality", false); + } + + virtual void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + SASSERT(g->is_well_sorted()); + mc = 0; pc = 0; core = 0; + m_01s->reset(); + + tactic_report report("cardinality-intro", *g); + + bound_manager bounds(m); + bounds(*g); + + + bound_manager::iterator bit = bounds.begin(), bend = bounds.end(); + for (; bit != bend; ++bit) { + expr* x = *bit; + bool s1, s2; + rational lo, hi; + if (a.is_int(x) && + bounds.has_lower(x, lo, s1) && !s1 && lo.is_zero() && + bounds.has_upper(x, hi, s2) && !s2 && hi.is_one()) { + m_01s->insert(x); + TRACE("pb", tout << "add bound " << mk_pp(x, m) << "\n";); + } + } + + expr_safe_replace sub(m); + extract_pb_substitution(g, sub); + + expr_ref new_curr(m); + proof_ref new_pr(m); + + for (unsigned i = 0; i < g->size(); i++) { + expr * curr = g->form(i); + sub(curr, new_curr); + if (m.proofs_enabled()) { + new_pr = m.mk_rewrite(curr, new_curr); + new_pr = m.mk_modus_ponens(g->pr(i), new_pr); + } + g->update(i, new_curr, new_pr, g->dep(i)); + } + g->inc_depth(); + result.push_back(g.get()); + TRACE("pb", g->display(tout);); + SASSERT(g->is_well_sorted()); + + // TBD: convert models for 0-1 variables. + // TBD: support proof conversion (or not..) + } + + void extract_pb_substitution(goal_ref const& g, expr_safe_replace& sub) { + ast_mark mark; + for (unsigned i = 0; i < g->size(); i++) { + extract_pb_substitution(mark, g->form(i), sub); + } + } + + void extract_pb_substitution(ast_mark& mark, expr* fml, expr_safe_replace& sub) { + expr_ref tmp(m); + m_todo->reset(); + m_todo->push_back(fml); + while (!m_todo->empty()) { + expr* e = m_todo->back(); + m_todo->pop_back(); + if (mark.is_marked(e) || !is_app(e)) { + continue; + } + mark.mark(e, true); + if (get_pb_relation(sub, e, tmp)) { + sub.insert(e, tmp); + continue; + } + app* ap = to_app(e); + m_todo->append(ap->get_num_args(), ap->get_args()); + } + } + + + bool is_01var(expr* x) const { + return m_01s->contains(x); + } + + expr_ref mk_01(expr* x) { + expr* r = m.mk_eq(x, a.mk_numeral(rational(1), m.get_sort(x))); + return expr_ref(r, m); + } + + bool get_pb_relation(expr_safe_replace& sub, expr* fml, expr_ref& result) { + expr* x, *y; + expr_ref_vector args(m); + vector coeffs; + rational coeff; + if ((a.is_le(fml, x, y) || a.is_ge(fml, y, x)) && + get_pb_sum(x, rational::one(), args, coeffs, coeff) && + get_pb_sum(y, -rational::one(), args, coeffs, coeff)) { + result = mk_le(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff); + return true; + } + else if ((a.is_lt(fml, y, x) || a.is_gt(fml, x, y)) && + get_pb_sum(x, rational::one(), args, coeffs, coeff) && + get_pb_sum(y, -rational::one(), args, coeffs, coeff)) { + result = m.mk_not(mk_le(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff)); + return true; + } + else if (m.is_eq(fml, x, y) && + get_pb_sum(x, rational::one(), args, coeffs, coeff) && + get_pb_sum(y, -rational::one(), args, coeffs, coeff)) { + result = mk_eq(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), -coeff); + return true; + } + return false; + } + + expr* mk_le(unsigned sz, rational const* weights, expr* const* args, rational const& w) { + if (sz == 0) { + return w.is_neg()?m.mk_false():m.mk_true(); + } + if (sz == 1 && weights[0].is_one() && w >= rational::one()) { + return m.mk_true(); + } + if (sz == 1 && weights[0].is_one() && w.is_zero()) { + return m.mk_not(args[0]); + } + return m_pb.mk_le(sz, weights, args, w); + } + + expr* mk_eq(unsigned sz, rational const* weights, expr* const* args, rational const& w) { + if (m_compile_equality) { + return m_pb.mk_eq(sz, weights, args, w); + } + else { + return m.mk_and(mk_ge(sz, weights, args, w), mk_le(sz, weights, args, w)); + } + } + + expr* mk_ge(unsigned sz, rational const* weights, expr* const* args, rational const& w) { + if (sz == 0) { + return w.is_pos()?m.mk_false():m.mk_true(); + } + if (sz == 1 && weights[0].is_one() && w.is_one()) { + return args[0]; + } + if (sz == 1 && weights[0].is_one() && w.is_zero()) { + return m.mk_not(args[0]); + } + return m_pb.mk_ge(sz, weights, args, w); + } + + bool get_pb_sum(expr* x, rational const& mul, expr_ref_vector& args, vector& coeffs, rational& coeff) { + expr *y, *z, *u; + rational r, q; + app* f = to_app(x); + bool ok = true; + if (a.is_add(x)) { + for (unsigned i = 0; ok && i < f->get_num_args(); ++i) { + ok = get_pb_sum(f->get_arg(i), mul, args, coeffs, coeff); + } + } + else if (a.is_sub(x, y, z)) { + ok = get_pb_sum(y, mul, args, coeffs, coeff); + ok = ok && get_pb_sum(z, -mul, args, coeffs, coeff); + } + else if (a.is_uminus(x, y)) { + ok = get_pb_sum(y, -mul, args, coeffs, coeff); + } + else if (a.is_mul(x, y, z) && is_numeral(y, r)) { + ok = get_pb_sum(z, r*mul, args, coeffs, coeff); + } + else if (a.is_mul(x, z, y) && is_numeral(y, r)) { + ok = get_pb_sum(z, r*mul, args, coeffs, coeff); + } + else if (a.is_to_real(x, y)) { + ok = get_pb_sum(y, mul, args, coeffs, coeff); + } + else if (m.is_ite(x, y, z, u) && is_numeral(z, r) && is_numeral(u, q)) { + insert_arg(r*mul, y, args, coeffs, coeff); + // q*(1-y) = -q*y + q + coeff += q*mul; + insert_arg(-q*mul, y, args, coeffs, coeff); + } + else if (is_01var(x)) { + insert_arg(mul, mk_01(x), args, coeffs, coeff); + } + else if (is_numeral(x, r)) { + coeff += mul*r; + } + else { + TRACE("pb", tout << "Can't handle " << mk_pp(x, m) << "\n";); + ok = false; + } + return ok; + } + + bool is_numeral(expr* e, rational& r) { + if (a.is_uminus(e, e) && is_numeral(e, r)) { + r.neg(); + return true; + } + if (a.is_to_real(e, e)) { + return is_numeral(e, r); + } + return a.is_numeral(e, r); + } + + void insert_arg(rational const& p, expr* x, + expr_ref_vector& args, vector& coeffs, rational& coeff) { + if (p.is_neg()) { + // p*x = -p*(1-x) + p + args.push_back(m.mk_not(x)); + coeffs.push_back(-p); + coeff += p; + } + else if (p.is_pos()) { + args.push_back(x); + coeffs.push_back(p); + } + } + + virtual tactic * translate(ast_manager & m) { + return alloc(lia2card_tactic, m, m_params); + } + + virtual void collect_param_descrs(param_descrs & r) { + r.insert("compile_equality", CPK_BOOL, + "(default:false) compile equalities into pseudo-Boolean equality"); + } + + virtual void cleanup() { + expr_set* d = alloc(expr_set); + ptr_vector* todo = alloc(ptr_vector); + #pragma omp critical (tactic_cancel) + { + std::swap(m_01s, d); + std::swap(m_todo, todo); + } + dealloc(d); + dealloc(todo); + } +}; + +tactic * mk_lia2card_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(lia2card_tactic, m, p)); +} + +bool get_pb_sum(expr* term, expr_ref_vector& args, vector& coeffs, rational& coeff) { + params_ref p; + ast_manager& m = args.get_manager(); + lia2card_tactic tac(m, p); + return tac.get_pb_sum(term, rational::one(), args, coeffs, coeff); +} diff --git a/src/tactic/arith/lia2card_tactic.h b/src/tactic/arith/lia2card_tactic.h new file mode 100644 index 000000000..de25ce409 --- /dev/null +++ b/src/tactic/arith/lia2card_tactic.h @@ -0,0 +1,35 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + lia2card_tactic.h + +Abstract: + + Extract 0-1 integer variables used in + cardinality constraints and replace them by Booleans. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-5 + +Notes: + +--*/ +#ifndef _LIA2CARD_TACTIC_H_ +#define _LIA2CARD_TACTIC_H_ + +#include"params.h" +class ast_manager; +class tactic; + +tactic * mk_lia2card_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* + ADD_TACTIC("lia2card", "introduce cardinality constraints from 0-1 integer.", "mk_lia2card_tactic(m, p)") +*/ + +bool get_pb_sum(expr* term, expr_ref_vector& args, vector& coeffs, rational& coeff); + +#endif diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index 33d5f138e..8d200c80d 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -293,9 +293,12 @@ class lia2pb_tactic : public tactic { m_rw(curr, new_curr, new_pr); if (m_produce_unsat_cores) { dep = m.mk_join(m_rw.get_used_dependencies(), g->dep(idx)); - m_rw.reset_used_dependencies(); + m_rw.reset_used_dependencies(); } - g->update(idx, new_curr, 0, dep); + if (m.proofs_enabled()) { + new_pr = m.mk_modus_ponens(g->pr(idx), new_pr); + } + g->update(idx, new_curr, new_pr, dep); } g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index 643195b05..1ef0efc47 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -854,7 +854,7 @@ private: m_temporary_ints(m), m_used_dependencies(m), m_rw(*this) { - updt_params(p); + updt_params(p); m_b_rw.set_flat(false); // no flattening otherwise will blowup the memory m_b_rw.set_elim_and(true); } @@ -871,12 +871,17 @@ private: m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_all_clauses_limit = p.get_uint("pb2bv_all_clauses_limit", 8); m_cardinality_limit = p.get_uint("pb2bv_cardinality_limit", UINT_MAX); + m_b_rw.updt_params(p); } void collect_param_descrs(param_descrs & r) { - insert_max_memory(r); + insert_max_memory(r); r.insert("pb2bv_all_clauses_limit", CPK_UINT, "(default: 8) maximum number of literals for using equivalent CNF encoding of PB constraint."); r.insert("pb2bv_cardinality_limit", CPK_UINT, "(default: inf) limit for using arc-consistent cardinality constraint encoding."); + + m_b_rw.get_param_descrs(r); + r.erase("flat"); + r.erase("elim_and"); } void set_cancel(bool f) { diff --git a/src/tactic/arith/probe_arith.cpp b/src/tactic/arith/probe_arith.cpp index 770281923..9c02b5f0f 100644 --- a/src/tactic/arith/probe_arith.cpp +++ b/src/tactic/arith/probe_arith.cpp @@ -513,6 +513,71 @@ static bool is_lira(goal const & g) { return !test(g, p); } + +struct is_non_qfufnra_functor { + struct found {}; + ast_manager & m; + arith_util u; + bool m_has_nonlinear; + + is_non_qfufnra_functor(ast_manager & _m): + m(_m), u(m), m_has_nonlinear(false) {} + + void throw_found() { + throw found(); + } + + bool has_nonlinear() const { + return m_has_nonlinear; + } + + void operator()(var * x) { + 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()) + return; + if (fid == u.get_family_id()) { + switch (n->get_decl_kind()) { + case OP_LE: case OP_GE: case OP_LT: case OP_GT: + case OP_ADD: case OP_UMINUS: case OP_SUB: case OP_ABS: + case OP_NUM: + case OP_IRRATIONAL_ALGEBRAIC_NUM: + return; + case OP_MUL: + if (n->get_num_args() == 2 && + u.is_real(n->get_arg(0)) && + !u.is_numeral(n->get_arg(0)) && + !u.is_numeral(n->get_arg(1))) { + m_has_nonlinear = true; + } + return; + case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: + if (!u.is_numeral(n->get_arg(1))) + throw_found(); + return; + case OP_POWER: + if (!u.is_numeral(n->get_arg(1))) + throw_found(); + m_has_nonlinear = true; + return; + case OP_IS_INT: + case OP_TO_INT: + case OP_TO_REAL: + throw_found(); + return; + default: + throw_found(); + } + } + } +}; + + class is_qfnia_probe : public probe { public: virtual result operator()(goal const & g) { @@ -569,6 +634,18 @@ public: } }; +static bool is_qfufnra(goal const& g) { + is_non_qfufnra_functor p(g.m()); + return !g.proofs_enabled() && !g.unsat_core_enabled() && !test(g, p) && p.has_nonlinear(); +} + +class is_qfufnra_probe : public probe { +public: + virtual result operator()(goal const & g) { + return is_qfufnra(g); + } +}; + probe * mk_is_qfnia_probe() { return alloc(is_qfnia_probe); } @@ -600,3 +677,7 @@ probe * mk_is_lra_probe() { probe * mk_is_lira_probe() { return alloc(is_lira_probe); } + +probe* mk_is_qfufnra_probe() { + return alloc(is_qfufnra_probe); +} diff --git a/src/tactic/arith/probe_arith.h b/src/tactic/arith/probe_arith.h index 83179098d..0ff64a0ba 100644 --- a/src/tactic/arith/probe_arith.h +++ b/src/tactic/arith/probe_arith.h @@ -55,6 +55,7 @@ probe * mk_is_nira_probe(); probe * mk_is_lia_probe(); probe * mk_is_lra_probe(); probe * mk_is_lira_probe(); +probe * mk_is_qfufnra_probe(); /* ADD_PROBE("is-qfnia", "true if the goal is in QF_NIA (quantifier-free nonlinear integer arithmetic).", "mk_is_qfnia_probe()") @@ -65,5 +66,6 @@ probe * mk_is_lira_probe(); ADD_PROBE("is-lia", "true if the goal is in LIA (linear integer arithmetic, formula may have quantifiers).", "mk_is_lia_probe()") ADD_PROBE("is-lra", "true if the goal is in LRA (linear real arithmetic, formula may have quantifiers).", "mk_is_lra_probe()") ADD_PROBE("is-lira", "true if the goal is in LIRA (linear integer and real arithmetic, formula may have quantifiers).", "mk_is_lira_probe()") + ADD_PROBE("is-qfufnra", "true if the goal is QF_UFNRA (quantifier-free nonlinear real arithmetic with other theories).", "mk_is_qfufnra_probe()") */ #endif diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index f2ddd86ce..e18c6e90b 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -707,7 +707,7 @@ public: virtual void collect_param_descrs(param_descrs & r) { r.insert("complete", CPK_BOOL, - "(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a functio. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root"); + "(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a function. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root"); r.insert("elim_root_objects", CPK_BOOL, "(default: true) eliminate root objects."); r.insert("elim_inverses", CPK_BOOL, diff --git a/src/tactic/bv/bit_blaster_tactic.cpp b/src/tactic/bv/bit_blaster_tactic.cpp index 33423c8b1..0391aad51 100644 --- a/src/tactic/bv/bit_blaster_tactic.cpp +++ b/src/tactic/bv/bit_blaster_tactic.cpp @@ -19,7 +19,7 @@ Notes: #include"tactical.h" #include"bit_blaster_model_converter.h" #include"bit_blaster_rewriter.h" -#include"ast_smt2_pp.h" +#include"ast_pp.h" #include"model_pp.h" #include"rewriter_types.h" @@ -69,6 +69,7 @@ class bit_blaster_tactic : public tactic { expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); + bool change = false; for (unsigned idx = 0; idx < size; idx++) { if (g->inconsistent()) break; @@ -79,10 +80,14 @@ class bit_blaster_tactic : public tactic { proof * pr = g->pr(idx); new_pr = m().mk_modus_ponens(pr, new_pr); } - g->update(idx, new_curr, new_pr, g->dep(idx)); + if (curr != new_curr) { + change = true; + TRACE("bit_blaster", tout << mk_pp(curr, m()) << " -> " << mk_pp(new_curr, m()) << "\n";); + g->update(idx, new_curr, new_pr, g->dep(idx)); + } } - if (g->models_enabled()) + if (change && g->models_enabled()) mc = mk_bit_blaster_model_converter(m(), m_rewriter.const2bits()); else mc = 0; diff --git a/src/tactic/core/blast_term_ite_tactic.cpp b/src/tactic/core/blast_term_ite_tactic.cpp new file mode 100644 index 000000000..fbc66c418 --- /dev/null +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -0,0 +1,223 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + blast_term_ite_tactic.cpp + +Abstract: + + Blast term if-then-else by hoisting them up. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-4 + +Notes: + +--*/ +#include"tactical.h" +#include"defined_names.h" +#include"rewriter_def.h" +#include"filter_model_converter.h" +#include"cooperate.h" +#include"scoped_proof.h" + + + +// +// (f (if c1 (if c2 e1 e2) e3) b c) -> +// (if c1 (if c2 (f e1 b c) +// + + +class blast_term_ite_tactic : public tactic { + + struct rw_cfg : public default_rewriter_cfg { + ast_manager& m; + unsigned long long m_max_memory; // in bytes + unsigned m_num_fresh; // number of expansions + + rw_cfg(ast_manager & _m, params_ref const & p): + m(_m), + m_num_fresh(0) { + updt_params(p); + } + + void updt_params(params_ref const & p) { + m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + } + + bool max_steps_exceeded(unsigned num_steps) const { + cooperate("blast term ite"); + // if (memory::get_allocation_size() > m_max_memory) + // throw tactic_exception(TACTIC_MAX_MEMORY_MSG); + return false; + } + + 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)) { + enable_trace("blast_term_ite"); + TRACE("blast_term_ite", result = m.mk_app(f, num_args, args); tout << result << "\n";); + expr_ref e1(m), e2(m); + ptr_vector args1(num_args, args); + args1[i] = t; + ++m_num_fresh; + 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; + } + + 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 mk_app_core(f, num, args, result); + } + + }; + + struct rw : public rewriter_tpl { + rw_cfg m_cfg; + + rw(ast_manager & m, params_ref const & p): + rewriter_tpl(m, m.proofs_enabled(), m_cfg), + m_cfg(m, p) { + } + }; + + struct imp { + ast_manager & m; + rw m_rw; + + imp(ast_manager & _m, params_ref const & p): + m(_m), + m_rw(m, p) { + } + + void set_cancel(bool f) { + m_rw.set_cancel(f); + } + + void updt_params(params_ref const & p) { + m_rw.cfg().updt_params(p); + } + + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + SASSERT(g->is_well_sorted()); + mc = 0; pc = 0; core = 0; + tactic_report report("blast-term-ite", *g); + bool produce_proofs = g->proofs_enabled(); + + expr_ref new_curr(m); + proof_ref new_pr(m); + unsigned size = g->size(); + for (unsigned idx = 0; idx < size; idx++) { + expr * curr = g->form(idx); + m_rw(curr, new_curr, new_pr); + if (produce_proofs) { + proof * pr = g->pr(idx); + new_pr = m.mk_modus_ponens(pr, new_pr); + } + g->update(idx, new_curr, new_pr, g->dep(idx)); + } + report_tactic_progress(":blast-term-ite-consts", m_rw.m_cfg.m_num_fresh); + g->inc_depth(); + result.push_back(g.get()); + TRACE("blast_term_ite", g->display(tout);); + SASSERT(g->is_well_sorted()); + } + }; + + imp * m_imp; + params_ref m_params; +public: + blast_term_ite_tactic(ast_manager & m, params_ref const & p): + m_params(p) { + m_imp = alloc(imp, m, p); + } + + virtual tactic * translate(ast_manager & m) { + return alloc(blast_term_ite_tactic, m, m_params); + } + + virtual ~blast_term_ite_tactic() { + dealloc(m_imp); + } + + virtual void updt_params(params_ref const & p) { + m_params = p; + m_imp->m_rw.cfg().updt_params(p); + } + + virtual void collect_param_descrs(param_descrs & r) { + insert_max_memory(r); + insert_max_steps(r); + r.insert("max_args", CPK_UINT, + "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); + } + + virtual void operator()(goal_ref const & in, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + (*m_imp)(in, result, mc, pc, core); + } + + virtual void cleanup() { + ast_manager & m = m_imp->m; + imp * d = m_imp; + #pragma omp critical (tactic_cancel) + { + m_imp = 0; + } + dealloc(d); + d = alloc(imp, m, m_params); + #pragma omp critical (tactic_cancel) + { + m_imp = d; + } + } + + virtual void set_cancel(bool f) { + if (m_imp) + m_imp->set_cancel(f); + } + + static void blast_term_ite(expr_ref& fml) { + ast_manager& m = fml.get_manager(); + scoped_no_proof _sp(m); + params_ref p; + rw ite_rw(m, p); + expr_ref tmp(m); + ite_rw(fml, tmp); + fml = tmp; + } +}; + +tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(blast_term_ite_tactic, m, p)); +} + +void blast_term_ite(expr_ref& fml) { + blast_term_ite_tactic::blast_term_ite(fml); +} diff --git a/src/tactic/core/blast_term_ite_tactic.h b/src/tactic/core/blast_term_ite_tactic.h new file mode 100644 index 000000000..6756b29d3 --- /dev/null +++ b/src/tactic/core/blast_term_ite_tactic.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + blast_term_ite_tactic.h + +Abstract: + + Blast term if-then-else by hoisting them up. + This is expensive but useful in some cases, such as + for enforcing constraints being in difference logic. + Use elim-term-ite elsewhere when possible. + + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-4 + +Notes: + +--*/ +#ifndef _BLAST_TERM_ITE_TACTIC_H_ +#define _BLAST_TERM_ITE_TACTIC_H_ + +#include"params.h" +class ast_manager; +class tactic; + +tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* + ADD_TACTIC("blast-term-ite", "blast term if-then-else by hoisting them.", "mk_blast_term_ite_tactic(m, p)") +*/ + +void blast_term_ite(expr_ref& fml); + +#endif diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp new file mode 100644 index 000000000..51e92ef11 --- /dev/null +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -0,0 +1,678 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pb_preprocess_tactic.cpp + +Abstract: + + Pre-process pseudo-Boolean inequalities using + generalized Davis Putnam (resolution) to eliminate variables. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-12-23 + +Notes: + + Resolution for PB constraints require the implicit + inequalities that each variable ranges over [0,1] + so not all resolvents produce smaller sets of clauses. + + We here implement subsumption resolution. + + x + y >= 1 + A~x + B~y + Cz >= k + --------------------- + Cz >= k - B + + where A <= B, x, y do not occur elsewhere. + + +--*/ +#include "pb_preprocess_tactic.h" +#include "tactical.h" +#include "for_each_expr.h" +#include "pb_decl_plugin.h" +#include "th_rewriter.h" +#include "expr_substitution.h" +#include "ast_pp.h" + +class pb_preproc_model_converter : public model_converter { + ast_manager& m; + pb_util pb; + expr_ref_vector m_refs; + svector > m_const; + +public: + pb_preproc_model_converter(ast_manager& m):m(m), pb(m), m_refs(m) {} + + virtual void operator()(model_ref & mdl, unsigned goal_idx) { + SASSERT(goal_idx == 0); + for (unsigned i = 0; i < m_const.size(); ++i) { + mdl->register_decl(m_const[i].first->get_decl(), m_const[i].second); + } + } + + void set_value(expr* e, bool p) { + while (m.is_not(e, e)) { + p = !p; + } + SASSERT(is_app(e)); + set_value_p(to_app(e), p?m.mk_true():m.mk_false()); + } + + virtual model_converter * translate(ast_translation & translator) { + pb_preproc_model_converter* mc = alloc(pb_preproc_model_converter, translator.to()); + for (unsigned i = 0; i < m_const.size(); ++i) { + mc->set_value_p(translator(m_const[i].first), translator(m_const[i].second)); + } + return mc; + } + +private: + void set_value_p(app* e, expr* v) { + SASSERT(e->get_num_args() == 0); + SASSERT(is_uninterp_const(e)); + m_const.push_back(std::make_pair(e, v)); + m_refs.push_back(e); + m_refs.push_back(v); + } + +}; + +class pb_preprocess_tactic : public tactic { + struct rec { unsigned_vector pos, neg; rec() { } }; + typedef obj_map var_map; + ast_manager& m; + pb_util pb; + var_map m_vars; + unsigned_vector m_ge; + unsigned_vector m_other; + bool m_progress; + th_rewriter m_r; + + + struct declassifier { + var_map& m_vars; + declassifier(var_map& v): m_vars(v) {} + + void operator()(app* e) { + if (m_vars.contains(e)) { + m_vars.remove(e); + } + } + void operator()(var*) {} + void operator()(quantifier*) {} + }; + + void display_annotation(std::ostream& out, goal_ref const& g) { + for (unsigned i = 0; i < m_ge.size(); ++i) { + out << "ge " << m_ge[i] << ": " << mk_pp(g->form(m_ge[i]), m) << "\n"; + } + for (unsigned i = 0; i < m_other.size(); ++i) { + out << "ot " << m_other[i] << ": " << mk_pp(g->form(m_other[i]), m) << "\n"; + } + + var_map::iterator it = m_vars.begin(); + var_map::iterator end = m_vars.end(); + for (; it != end; ++it) { + app* e = it->m_key; + unsigned_vector const& pos = it->m_value.pos; + unsigned_vector const& neg = it->m_value.neg; + out << mk_pp(e, m) << ": "; + for (unsigned i = 0; i < pos.size(); ++i) { + out << "p: " << pos[i] << " "; + } + for (unsigned i = 0; i < neg.size(); ++i) { + out << "n: " << neg[i] << " "; + } + out << "\n"; + } + } + +public: + pb_preprocess_tactic(ast_manager& m, params_ref const& p = params_ref()): + m(m), pb(m), m_r(m) {} + + virtual ~pb_preprocess_tactic() {} + + virtual tactic * translate(ast_manager & m) { + return alloc(pb_preprocess_tactic, m); + } + + virtual void operator()( + goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + SASSERT(g->is_well_sorted()); + pc = 0; core = 0; + + if (g->unsat_core_enabled()) { + throw tactic_exception("pb-preprocess does not support cores"); + } + if (g->proofs_enabled()) { + throw tactic_exception("pb-preprocess does not support proofs"); + } + + pb_preproc_model_converter* pp = alloc(pb_preproc_model_converter, m); + mc = pp; + + g->inc_depth(); + result.push_back(g.get()); + while (simplify(g, *pp)); + // decompose(g); + } + + bool simplify(goal_ref const& g, pb_preproc_model_converter& mc) { + reset(); + normalize(g); + if (g->inconsistent()) { + return false; + } + for (unsigned i = 0; i < g->size(); ++i) { + process_vars(i, g); + } + + if (m_ge.empty()) { + return false; + } + + for (unsigned i = 0; i < m_ge.size(); ++i) { + classify_vars(i, to_app(g->form(m_ge[i]))); + } + + declassifier dcl(m_vars); + expr_mark visited; + for (unsigned i = 0; !m_vars.empty() && i < m_other.size(); ++i) { + for_each_expr(dcl, visited, g->form(m_other[i])); + } + + if (m_vars.empty()) { + return false; + } + + // display_annotation(tout, g); + m_progress = false; + // first eliminate variables + var_map::iterator it = next_resolvent(m_vars.begin()); + while (it != m_vars.end()) { + app * e = it->m_key; + rec const& r = it->m_value; + if (r.pos.empty()) { + replace(r.neg, e, m.mk_false(), g); + mc.set_value(e, false); + } + else if (r.neg.empty()) { + replace(r.pos, e, m.mk_true(), g); + mc.set_value(e, true); + } + if (g->inconsistent()) return false; + ++it; + it = next_resolvent(it); + } + // now resolve clauses. + it = next_resolvent(m_vars.begin()); + while (it != m_vars.end()) { + + app * e = it->m_key; + SASSERT(is_uninterp_const(e)); + rec const& r = it->m_value; + if (r.pos.size() == 1 && !r.neg.empty()) { + resolve(mc, r.pos[0], r.neg, e, true, g); + } + else if (r.neg.size() == 1 && !r.pos.empty()) { + resolve(mc, r.neg[0], r.pos, e, false, g); + } + if (g->inconsistent()) return false; + ++it; + it = next_resolvent(it); + } + + // now check for subsumption. + for (unsigned i = 0; i < m_ge.size(); ++i) { + + expr_ref_vector args1(m), args2(m); + vector coeffs1, coeffs2; + rational k1, k2; + expr* fml = g->form(m_ge[i]); + if (!to_ge(fml, args1, coeffs1, k1)) continue; + if (args1.empty()) continue; + expr* arg = args1[0].get(); + bool neg = m.is_not(arg, arg); + if (!is_uninterp_const(arg)) continue; + if (!m_vars.contains(to_app(arg))) continue; + rec const& r = m_vars.find(to_app(arg)); + unsigned_vector const& pos = neg?r.neg:r.pos; + for (unsigned j = 0; j < pos.size(); ++j) { + unsigned k = pos[j]; + if (k == i) continue; + if (!to_ge(g->form(k), args2, coeffs2, k2)) continue; + if (subsumes(args1, coeffs1, k1, args2, coeffs2, k2)) { + IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(g->form(k), m) << "\n";); + g->update(k, m.mk_true()); + m_progress = true; + } + } + } + + g->elim_true(); + + return m_progress; + } + + virtual void set_cancel(bool f) { + } + + virtual void updt_params(params_ref const & p) { + } + + virtual void cleanup() { + } + +private: + + void reset() { + m_ge.reset(); + m_other.reset(); + m_vars.reset(); + } + + expr* negate(expr* e) { + if (m.is_not(e, e)) return e; + return m.mk_not(e); + } + + void normalize(goal_ref const& g) { + expr* r; + expr_ref tmp(m); + for (unsigned i = 0; !g->inconsistent() && i < g->size(); ++i) { + expr* e = g->form(i); + if (m.is_not(e, r) && pb.is_ge(r)) { + rational k = pb.get_k(r); + rational sum(0); + expr_ref_vector args(m); + vector coeffs; + for (unsigned j = 0; j < to_app(r)->get_num_args(); ++j) { + sum += pb.get_coeff(r, j); + coeffs.push_back(pb.get_coeff(r, j)); + args.push_back(negate(to_app(r)->get_arg(j))); + } + tmp = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), sum - k + rational::one()); + g->update(i, tmp); + } + } + } + + unsigned log2ceil(unsigned n) { + unsigned p = 1; + while (n > 0) { + n /= 2; + ++p; + } + return p; + } + + /** + \brief decompose large sums into smaller sums by intoducing + auxilary variables. + */ + void decompose(goal_ref const& g) { + expr_ref fml1(m), fml2(m); + for (unsigned i = 0; !g->inconsistent() && i < g->size(); ++i) { + expr* e = g->form(i); + unsigned_vector cuts; + if (cut(e, cuts)) { + app* a = to_app(e); + expr_ref_vector cut_args(m); + vector cut_coeffs; + if (cuts.size() < 2) continue; + unsigned start = 0; + for (unsigned j = 0; j < cuts.size(); ++j) { + unsigned end = cuts[j]; + fml1 = decompose_cut(a, start, end, cut_args, cut_coeffs); + g->assert_expr(fml1); + start = end; + TRACE("pb", tout << fml1 << "\n";); + } + fml2 = pb.mk_ge(cut_args.size(), cut_coeffs.c_ptr(), cut_args.c_ptr(), pb.get_k(e)); + g->update(i, fml2); + TRACE("pb", tout << fml2 << "\n";); + } + } + } + bool cut(expr* e, unsigned_vector& cuts) { + if (!pb.is_ge(e)) return false; + if (to_app(e)->get_num_args() <= 20) return false; + unsigned n = 0, cut = 0; + unsigned sz = to_app(e)->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + rational r = pb.get_coeff(e, i); + if (!r.is_unsigned()) { + return false; + } + n += r.get_unsigned(); + if (2*log2ceil(n) < cut) { + cuts.push_back(i+1); + n = 0; + cut = 0; + } + else { + ++cut; + } + } + if (!cuts.empty() && cuts.back() + 20 >= sz) { + cuts.pop_back(); + } + cuts.push_back(sz); + return true; + } + + expr_ref decompose_cut(app* e, unsigned start, unsigned end, + expr_ref_vector& cut_args, + vector& cut_coeffs) { + unsigned n = 0, j = 1; + vector coeffs; + expr_ref_vector args(m); + app_ref z(m); + expr_ref fml1(m), fml(m); + + for (unsigned i = start; i < end; ++i) { + rational r = pb.get_coeff(e, i); + n += r.get_unsigned(); + args.push_back(e->get_arg(i)); + coeffs.push_back(r); + } + + while (j <= n) { + z = m.mk_fresh_const("z", m.mk_bool_sort()); + coeffs.push_back(-rational(j)); + args.push_back(z); + cut_coeffs.push_back(rational(j)); + cut_args.push_back(z); + j <<= 1; + } + fml1 = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), rational(0)); + m_r(fml1, fml); + return fml; + } + + + void process_vars(unsigned i, goal_ref const& g) { + expr* r, *e; + e = g->form(i); + + if (is_uninterp_const(e)) { + m_ge.push_back(i); + } + else if (pb.is_ge(e) && pure_args(to_app(e))) { + m_ge.push_back(i); + } + else if (m.is_or(e) && pure_args(to_app(e))) { + m_ge.push_back(i); + } + else if (m.is_not(e, r) && is_uninterp_const(r)) { + m_ge.push_back(i); + } + else { + m_other.push_back(i); + } + } + + void classify_vars(unsigned idx, app* e) { + expr* r; + if (m.is_not(e, r) && is_uninterp_const(r)) { + insert(idx, to_app(r), false); + return; + } + if (is_uninterp_const(e)) { + insert(idx, e, true); + return; + } + for (unsigned i = 0; i < e->get_num_args(); ++i) { + expr* arg = e->get_arg(i); + if (m.is_true(arg) || m.is_false(arg)) { + // no-op + } + else if (m.is_not(arg, r)) { + SASSERT(is_uninterp_const(r)); + insert(idx, to_app(r), false); + } + else { + SASSERT(is_uninterp_const(arg)); + insert(idx, to_app(arg), true); + } + } + } + + void insert(unsigned i, app* e, bool pos) { + SASSERT(is_uninterp_const(e)); + if (!m_vars.contains(e)) { + m_vars.insert(e, rec()); + } + if (pos) { + m_vars.find(e).pos.push_back(i); + } + else { + m_vars.find(e).neg.push_back(i); + } + } + + bool pure_args(app* a) const { + for (unsigned i = 0; i < a->get_num_args(); ++i) { + expr* e = a->get_arg(i); + m.is_not(e, e); + if (!is_uninterp_const(e) && !m.is_true(e) && !m.is_false(e)) { + return false; + } + } + return true; + } + + var_map::iterator next_resolvent(var_map::iterator it) { + if (it == m_vars.end()) { + return it; + } + while (it != m_vars.end() && it->m_value.pos.size() > 1 && it->m_value.neg.size() > 1) { + ++it; + } + return it; + } + + rational get_coeff(unsigned num_args, expr* const* args, rational const* coeffs, expr* e) { + for (unsigned i = 0; i < num_args; ++i) { + if (args[i] == e) return coeffs[i]; + } + return rational::zero(); + } + + // + // one of the formulas are replaced by T after resolution + // so if there is a pointer into that formula, we can no + // longer assume variables have unique occurrences. + // + bool is_valid(unsigned_vector const& positions, goal_ref const& g) const { + for (unsigned i = 0; i < positions.size(); ++i) { + unsigned idx = positions[i]; + if (m.is_true(g->form(idx))) return false; + } + return true; + } + + bool is_reduction(unsigned_vector const& pos, app* fml, goal_ref const& g) { + unsigned sz = fml->get_num_args(); + for (unsigned i = 0; i < pos.size(); ++i) { + if (!is_app(g->form(pos[i]))) return false; + if (to_app(g->form(pos[i]))->get_num_args() < sz) return false; + } + return true; + } + + // Implement very special case of resolution. + + void resolve(pb_preproc_model_converter& mc, unsigned idx1, + unsigned_vector const& positions, app* e, bool pos, goal_ref const& g) { + if (positions.size() != 1) return; + unsigned idx2 = positions[0]; + expr_ref tmp1(m), tmp2(m); + expr* fml1 = g->form(idx1); + expr* fml2 = g->form(idx2); + TRACE("pb", tout << mk_pp(fml1, m) << " " << mk_pp(fml2, m) << "\n";); + expr_ref_vector args1(m), args2(m); + vector coeffs1, coeffs2; + rational k1, k2; + if (!to_ge(fml1, args1, coeffs1, k1)) return; + if (!k1.is_one()) return; + if (!to_ge(g->form(idx2), args2, coeffs2, k2)) return; + // check that each variable in idx1 occurs only in idx2 + unsigned min_index = 0; + rational min_coeff(0); + unsigned_vector indices; + for (unsigned i = 0; i < args1.size(); ++i) { + expr* x = args1[i].get(); + m.is_not(x, x); + if (!is_app(x)) return; + if (!m_vars.contains(to_app(x))) return; + TRACE("pb", tout << mk_pp(x, m) << "\n";); + rec const& r = m_vars.find(to_app(x)); + if (r.pos.size() != 1 || r.neg.size() != 1) return; + if (r.pos[0] != idx2 && r.neg[0] != idx2) return; + for (unsigned j = 0; j < args2.size(); ++j) { + if (is_complement(args1[i].get(), args2[j].get())) { + if (i == 0) { + min_coeff = coeffs2[j]; + } + else if (min_coeff > coeffs2[j]) { + min_coeff = coeffs2[j]; + min_index = j; + } + indices.push_back(j); + } + } + } + for (unsigned i = 0; i < indices.size(); ++i) { + unsigned j = indices[i]; + expr* arg = args2[j].get(); + if (j == min_index) { + args2[j] = m.mk_false(); + } + else { + args2[j] = m.mk_true(); + } + mc.set_value(arg, j != min_index); + } + + tmp1 = pb.mk_ge(args2.size(), coeffs2.c_ptr(), args2.c_ptr(), k2); + IF_VERBOSE(3, verbose_stream() << " " << tmp1 << "\n"; + for (unsigned i = 0; i < args2.size(); ++i) { + verbose_stream() << mk_pp(args2[i].get(), m) << " "; + } + verbose_stream() << "\n"; + ); + m_r(tmp1, tmp2); + if (pb.is_ge(tmp2) && pb.get_k(to_app(tmp2)).is_one()) { + tmp2 = m.mk_or(to_app(tmp2)->get_num_args(), to_app(tmp2)->get_args()); + } + IF_VERBOSE(3, + verbose_stream() << "resolve: " << mk_pp(fml1, m) << "\n" << mk_pp(fml2, m) << "\n" << tmp1 << "\n"; + verbose_stream() << "to\n" << mk_pp(fml2, m) << " -> " << tmp2 << "\n";); + + g->update(idx1, m.mk_true()); // proof & dependencies + g->update(idx2, tmp2); // proof & dependencies + m_progress = true; + //IF_VERBOSE(0, if (!g->inconsistent()) display_annotation(verbose_stream(), g);); + } + + bool is_complement(expr* x, expr* y) const { + if (m.is_not(x,x)) return x == y; + if (m.is_not(y,y)) return x == y; + return false; + } + + bool to_ge(expr* e, expr_ref_vector& args, vector& coeffs, rational& k) { + expr* r; + if (is_uninterp_const(e)) { + args.push_back(e); + coeffs.push_back(rational::one()); + k = rational::one(); + } + else if (m.is_not(e, r) && is_uninterp_const(r)) { + args.push_back(e); + coeffs.push_back(rational::one()); + k = rational::one(); + } + else if (pb.is_ge(e)) { + app* a = to_app(e); + SASSERT(pure_args(a)); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + args.push_back(a->get_arg(i)); + coeffs.push_back(pb.get_coeff(a, i)); + } + k = pb.get_k(e); + } + else if (m.is_or(e)) { + app* a = to_app(e); + SASSERT(pure_args(a)); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + args.push_back(a->get_arg(i)); + coeffs.push_back(rational::one()); + } + k = rational::one(); + } + else { + return false; + } + return true; + } + + void replace(unsigned_vector const& positions, expr* e, expr* v, goal_ref const& g) { + if (!is_valid(positions, g)) return; + expr_substitution sub(m); + sub.insert(e, v); + expr_ref tmp(m); + m_r.set_substitution(&sub); + for (unsigned i = 0; i < positions.size(); ++i) { + unsigned idx = positions[i]; + expr_ref f(m); + f = g->form(idx); + if (!m.is_true(f)) { + m_r(f, tmp); + if (tmp != f) { + TRACE("pb", tout << mk_pp(f, m) << " -> " << tmp + << " by " << mk_pp(e, m) << " |-> " << mk_pp(v, m) << "\n";); + IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(f, m) << " -> " << tmp << "\n";); + g->update(idx, tmp); // proof & dependencies. + m_progress = true; + } + } + } + m_r.set_substitution(0); + } + + bool subsumes(expr_ref_vector const& args1, + vector const& coeffs1, rational const& k1, + expr_ref_vector const& args2, + vector const& coeffs2, rational const& k2) { + if (k2 > k1) return false; + for (unsigned i = 0; i < args1.size(); ++i) { + bool found = false; + for (unsigned j = 0; !found && j < args2.size(); ++j) { + if (args1[i] == args2[j]) { + if (coeffs1[i] > coeffs2[j]) return false; + found = true; + } + } + if (!found) return false; + } + return true; + } +}; + + +tactic * mk_pb_preprocess_tactic(ast_manager & m, params_ref const & p) { + return alloc(pb_preprocess_tactic, m); +} + diff --git a/src/tactic/core/pb_preprocess_tactic.h b/src/tactic/core/pb_preprocess_tactic.h new file mode 100644 index 000000000..5746779b7 --- /dev/null +++ b/src/tactic/core/pb_preprocess_tactic.h @@ -0,0 +1,34 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + pb_preprocess_tactic.h + +Abstract: + + Pre-process pseudo-Boolean inequalities using + generalized Davis Putnam (resolution) to eliminate variables. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-12-23 + +Notes: + +--*/ +#ifndef _PB_PREPROCESS_TACTIC_H_ +#define _PB_PREPROCESS_TACTIC_H_ + +#include"params.h" +class ast_manager; +class tactic; + +tactic * mk_pb_preprocess_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* + ADD_TACTIC("pb-preprocess", "pre-process pseudo-Boolean constraints a la Davis Putnam.", "mk_pb_preprocess_tactic(m, p)") +*/ + + +#endif diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 5873efd61..39df3b174 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -135,6 +135,7 @@ class propagate_values_tactic : public tactic { TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m()) << "\n---->\n" << mk_ismt2_pp(new_curr, m()) << "\n";); push_result(new_curr, new_pr); + if (new_curr != curr) m_modified = true; } @@ -160,6 +161,9 @@ class propagate_values_tactic : public tactic { if (m_goal->inconsistent()) goto end; + if (m_max_rounds == 0) + goto end; + m_subst = alloc(expr_substitution, m(), g->unsat_core_enabled(), g->proofs_enabled()); m_r.set_substitution(m_subst.get()); m_occs(*m_goal); diff --git a/src/tactic/core/simplify_tactic.cpp b/src/tactic/core/simplify_tactic.cpp index 39f10e10c..80be97b0b 100644 --- a/src/tactic/core/simplify_tactic.cpp +++ b/src/tactic/core/simplify_tactic.cpp @@ -31,6 +31,9 @@ struct simplify_tactic::imp { m_num_steps(0) { } + ~imp() { + } + ast_manager & m() const { return m_manager; } void set_cancel(bool f) { diff --git a/src/tactic/fpa/const_intro_rewriter.h b/src/tactic/fpa/const_intro_rewriter.h new file mode 100644 index 000000000..5c4675fbb --- /dev/null +++ b/src/tactic/fpa/const_intro_rewriter.h @@ -0,0 +1,150 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + const_intro_rewriter.h + +Abstract: + + Rewriter for converting FPA to BV + +Author: + + Christoph (cwinter) 2012-02-09 + +Notes: + +--*/ + +#ifndef _CONST_INTRO_REWRITER_H_ +#define _CONST_INTRO_REWRITER_H_ + +#include"cooperate.h" +#include"bv_decl_plugin.h" +#include"tactic_exception.h" +#include"fpa2bv_converter_prec.h" + +struct const_intro_rewriter_cfg : public default_rewriter_cfg { + ast_manager & m_manager; + + expr * m_exp; + func_decl_ref_vector m_introduced_consts; + obj_map m_const2term_map; + + unsigned long long m_max_memory; + unsigned m_max_steps; + + fpa_util m_float_util; + + ast_manager & m() const { return m_manager; } + + const_intro_rewriter_cfg(ast_manager & m, params_ref const & p): + m_manager(m), + m_introduced_consts(m), + m_float_util(m) { + updt_params(p); + // We need to make sure that the mananger has the BV plugin loaded. + symbol s_bv("bv"); + if (!m_manager.has_plugin(s_bv)) + m_manager.register_plugin(s_bv, alloc(bv_decl_plugin)); + } + + ~const_intro_rewriter_cfg() { + for (obj_map::iterator it = m_const2term_map.begin(); + it != m_const2term_map.end(); + it++) + { + m().dec_ref(it->m_key); + m().dec_ref(it->m_value); + } + } + + void cleanup_buffers() { + } + + void updt_params(params_ref const & p) { + m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_max_steps = p.get_uint("max_steps", UINT_MAX); + } + + bool max_steps_exceeded(unsigned num_steps) const { + cooperate("fpa2bv"); + if (memory::get_allocation_size() > m_max_memory) + throw tactic_exception(TACTIC_MAX_MEMORY_MSG); + return num_steps > m_max_steps; + } + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + TRACE("fpa2bv_rw", tout << "APP: " << f->get_name() << std::endl; ); + + if (num == 0 && f->get_family_id() == null_family_id && m_float_util.is_float(f->get_range())) { + app * f_cnst = m_manager.mk_const(f); + if (!m_introduced_consts.contains(f)) + m_introduced_consts.push_back(f); + result = f_cnst; + return BR_DONE; + } + + if (f->get_family_id() == m_float_util.get_family_id()) { + switch (f->get_decl_kind()) { + case OP_FPA_ADD: + case OP_FPA_SUB: + case OP_FPA_NEG: + case OP_FPA_MUL: + case OP_FPA_DIV: + case OP_FPA_REM: + case OP_FPA_ABS: + case OP_FPA_MIN: + case OP_FPA_MAX: + case OP_FPA_FMA: + case OP_FPA_SQRT: + case OP_FPA_TO_FP: + case OP_FPA_ROUND_TO_INTEGRAL: + { + app * f_app = m_manager.mk_app(f, num, args); + result = m_manager.mk_fresh_const(NULL, f->get_range()); + func_decl * fd = to_app(result)->get_decl(); + m_introduced_consts.push_back(fd); + m_const2term_map.insert_if_not_there(fd, f_app); + m().inc_ref(fd); + m().inc_ref(f_app); + return BR_DONE; + } + default: + return BR_FAILED; + } + } + + return BR_FAILED; + } + + bool reduce_quantifier(quantifier * old_q, + expr * new_body, + expr * const * new_patterns, + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr) { + return false; + } + + bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { + return false; + } + + bool pre_visit(expr * t){ + return true; + } +}; + +template class rewriter_tpl; + +struct const_intro_rewriter : public rewriter_tpl { + const_intro_rewriter_cfg m_cfg; + const_intro_rewriter(ast_manager & m, params_ref const & p): + rewriter_tpl(m, m.proofs_enabled(), m_cfg), + m_cfg(m, p) { + } +}; + +#endif diff --git a/src/tactic/fpa/fpa2bv_approx_tactic.cpp b/src/tactic/fpa/fpa2bv_approx_tactic.cpp new file mode 100644 index 000000000..4d52a11ca --- /dev/null +++ b/src/tactic/fpa/fpa2bv_approx_tactic.cpp @@ -0,0 +1,1313 @@ +/*++ + Copyright (c) 2012 Microsoft Corporation + + Module Name: + + fpa2bv_approx_tactic.cpp + + Abstract: + + Tactic that converts floating points to bit-vectors lazily + + Author: + + Aleksander Zeljic 2012-11-15 + + Notes: + + --*/ +#include"tactical.h" +#include"cooperate.h" +#include"ref_util.h" + +#include"th_rewriter.h" +#include"bit_blaster_rewriter.h" +#include"bit_blaster_model_converter.h" +#include"model_v2_pp.h" +#include"goal2sat.h" +#include"sat_solver.h" +#include"fpa_decl_plugin.h" +#include"fpa2bv_converter_prec.h" +#include"fpa2bv_model_converter.h" +#include"fpa2bv_converter.h" +#include"propagate_values_tactic.h" +#include"fpa2bv_rewriter_prec.h" +#include"fpa2bv_approx_tactic.h" +#include"const_intro_rewriter.h" +#include"ctx_simplify_tactic.h" +#include"filter_model_converter.h" +#include +#include +#include + +#include + +#define K_MIN 10 +#define K_PERCENTAGE 0.3 +#define PREC_INCREMENT 20 +#define ERR_OP 0 // + +struct pair +{ + expr * exp; + double quotient;// mpf * +}; + +bool isinfinite(double x) { +#ifdef _WIN32 + int c = _fpclass(x); + return c == _FPCLASS_PINF || c == _FPCLASS_NINF; +#else + return fpclassify(x) == FP_INFINITE; +#endif +} + +class fpa2bv_approx_tactic: public tactic { + struct imp { + ast_manager & m; + goal2sat m_goal2sat; + sat2goal m_sat2goal; + params_ref m_params; + unsigned m_num_steps; + bool m_proofs_enabled; + bool m_produce_models; + bool m_produce_unsat_cores; + bool m_cancel; + + fpa_approximation_mode m_mode; + ast_manager * m_temp_manager; + model_ref m_fpa_model; + fpa_util m_float_util; + + imp(ast_manager & _m, params_ref const & p, fpa_approximation_mode mode) : + m(_m), + m_params(p), + m_proofs_enabled(false), + m_produce_models(false), + m_produce_unsat_cores(false), + m_cancel(false), + m_mode(mode), + m_temp_manager(0), + m_float_util(_m) { + } + + void updt_params(params_ref const & p) { + m_params = p; + } + + void set_cancel(bool f) { + //If f is true stop everything + m_cancel = f; + } + + void init_precision_mapping(func_decl_ref_vector const & cnsts, + obj_map & map, + obj_map & const2term_map) { + for (unsigned i = 0; i < cnsts.size(); i++) + { + if (const2term_map.contains(cnsts.get(i)) || m_mode == FPAA_SMALL_FLOATS) + map.insert_if_not_there(cnsts.get(i), 0); + else + map.insert_if_not_there(cnsts.get(i), MAX_PRECISION); + } + } + + bool proof_guided_refinement( + goal_ref const & g, + func_decl_ref_vector const & cnsts, + obj_map & cnst2prec_map, + obj_map & new_map) + { + // We have no model. Let's just increase precision of everything. + bool res = false; + for (unsigned i = 0; i < cnsts.size(); i++) + { + unsigned old = cnst2prec_map.find(cnsts.get(i)); + unsigned n = old + PREC_INCREMENT; + if (old >= MAX_PRECISION) n = MAX_PRECISION; + else { if (n > MAX_PRECISION) n = MAX_PRECISION; res = true; } + new_map.insert(cnsts.get(i), n); + } + return res; + } + + void boolean_comparison_of_models(goal_ref g, model_ref const & mdl, model_ref const & full_mdl, obj_map & cnst2term_map, obj_map& count) + { + std::queue to_traverse; + app * cur; + int cur_cnt; + + expr_ref mdl_eval(m), full_eval(m); + + for (unsigned i=0; i < g->size(); i++){ + mdl->eval(g->form(i),mdl_eval,true); + full_mdl->eval(g->form(i),full_eval,true); + + //Push only if the full model evaluates to false, or if the models differ? + if (!m.is_true(full_eval)) // m.is_true(full_eval) != m.is_true(mdl_eval) + to_traverse.push(g->form(i)); + } + + while (to_traverse.size() > 0) { + cur = to_app(to_traverse.front()); +#ifdef Z3DEBUG + std::cout<<"Analyze - traversing: "<get_decl())) + count.insert(cur,1); + + if(cnst2term_map.contains(cur->get_decl())) + to_traverse.push(cnst2term_map.find(cur->get_decl())); + + for(unsigned i=0;iget_num_args();i++) { + if(m_float_util.is_rm(cur->get_arg(i)) || m_float_util.is_numeral(cur->get_arg(i))) + continue; + to_traverse.push(cur->get_arg(i)); + } + } + else { //Comparing boolean values from the model and the expanded model + mdl->eval(cur,mdl_eval,true); + full_mdl->eval(cur,full_eval,true); + + + if (m.is_true(full_eval) != m.is_true(mdl_eval)) { + //queue arguments + for(unsigned i=0; i < cur->get_num_args(); i++) + to_traverse.push(cur->get_arg(i)); + } + } + to_traverse.pop(); + } +#ifdef Z3DEBUG + std::cout<<"Expression count"<::iterator it = count.begin(); + it!= count.end(); + it++) { + std::cout<m_key,m)<<":"<m_value<size();i++) { + eq = to_app(g->form(i)); + + if (eq->get_family_id() == m.get_basic_family_id() && + eq->get_decl_kind() == OP_EQ){ + //eq is in fact an equality + app * lhs = to_app(eq->get_arg(0)); + app * rhs = to_app(eq->get_arg(1)); + expr * lhs_e,*rhs_e,*exp, *exp_e; + app *other = NULL; + + + if(lhs->get_num_args()==0 && + rhs ->get_num_args()==0){ + //over constants + lhs_e = full_mdl->get_const_interp(lhs->get_decl()); + rhs_e = full_mdl->get_const_interp(rhs->get_decl()); + + // != would work as well, to make sure they are not both NULL, + //and could simplify later checks + if(lhs_e != rhs_e) { //SASSERT(lhs_e || rhs_e); + //and one is registered in the full model while the other is not + if(!lhs_e){// && rhs_e){ + other = lhs; + exp_e = rhs_e; + exp = rhs; + } + else { // if(!rhs_e && lhs_e){ + other = rhs; + exp_e = lhs_e; + exp = lhs; + } + full_mdl->register_decl(other->get_decl(),exp_e); + +#ifdef Z3DEBUG + std::cout< & cnst2term_map, + obj_map & precise_op, + obj_map & actual_value, + obj_map & err_est, + mpf_rounding_mode & rm, + bool & precise_children, + bool & seen_all_children, + bool & children_have_finite_err, + mpf * arg_val, + mpf * est_arg_val + //expr_ref * arg_e + ){ + + expr_ref arg_e[] = { expr_ref(m), expr_ref(m), expr_ref(m), expr_ref(m) }; + unsigned i=0; + //Set rounding mode + if (rhs->get_num_args() > 0 && m_float_util.is_rm(rhs->get_arg(0))) { + expr_ref rm_val(m); + mdl->eval(rhs->get_arg(0), rm_val, true); + m_float_util.is_rm_numeral(rm_val, rm); + i = 1; + } + //Collect argument values + for (; i < rhs->get_num_args(); i++) { + expr * arg = rhs->get_arg(i); + + if (is_app(arg) && to_app(arg)->get_num_args() == 0) { + if (precise_op.contains(arg)) { + precise_children &= precise_op.find(arg); + } + else if (!cnst2term_map.contains(to_app(arg)->get_decl())) { + /* that's okay */ + } + else { +#ifdef Z3DEBUG + std::cout << "Not seen all children of " << mk_ismt2_pp(rhs, m) << + " (spec. " << mk_ismt2_pp(arg, m) << ")" << std::endl; +#endif + precise_children = false; + seen_all_children = false; + break; + } + } + + // Value from small model + mdl->eval(arg, arg_e[i],true); + m_float_util.is_numeral(arg_e[i], arg_val[i]); + + if( children_have_finite_err && + err_est.contains(arg) && + isinfinite(err_est.find(arg))) + children_have_finite_err=false; + + if (actual_value.contains(arg)) + mpf_mngr.set(est_arg_val[i], *actual_value.find(arg)); + else if (seen_all_children && is_app(arg) && to_app(arg)->get_num_args()==0) { + //We have seen all children so if it is a constant and not in actual_value then + //it is an input variable and its est_val is the same as actual value + mpf * tmp = alloc(mpf); + mpf_mngr.set(*tmp, arg_val[i]); + actual_value.insert(arg, tmp); + mpf_mngr.set(est_arg_val[i], *tmp); + } + else + std::cout << "Estimated value missing: " << mk_ismt2_pp(arg,m) << std::endl; + } + + } + + + void full_semantics_eval( + app * rhs, + mpf_manager & mpf_mngr, + mpf_rounding_mode & rm, + mpf * arg_val, + mpf * est_arg_val, + mpf & rhs_value, + mpf & est_rhs_value){ + + switch (rhs->get_decl()->get_decl_kind()) { + case OP_FPA_ADD: + mpf_mngr.add(rm, arg_val[1], arg_val[2], rhs_value); + mpf_mngr.add(rm, est_arg_val[1], est_arg_val[2], est_rhs_value); + break; + case OP_FPA_SUB: + mpf_mngr.sub(rm, arg_val[1], arg_val[2], rhs_value); + mpf_mngr.sub(rm, est_arg_val[1], est_arg_val[2], est_rhs_value); + break; + case OP_FPA_NEG: + mpf_mngr.neg(arg_val[0], rhs_value); + mpf_mngr.neg(est_arg_val[0], est_rhs_value);//Does it even make sense to look at this? + break; + case OP_FPA_MUL: + mpf_mngr.mul(rm, arg_val[1], arg_val[2], rhs_value); + mpf_mngr.mul(rm, est_arg_val[1], est_arg_val[2], est_rhs_value); + break; + case OP_FPA_DIV: + mpf_mngr.div(rm, arg_val[1], arg_val[2], rhs_value); + mpf_mngr.div(rm, est_arg_val[1], est_arg_val[2], est_rhs_value); + break; + case OP_FPA_REM: + mpf_mngr.rem(arg_val[0], arg_val[1], rhs_value); + mpf_mngr.rem(est_arg_val[0], est_arg_val[1], est_rhs_value); + break; + case OP_FPA_FMA: + mpf_mngr.fused_mul_add(rm, arg_val[1], arg_val[2], arg_val[3], rhs_value); + mpf_mngr.fused_mul_add(rm, est_arg_val[1], est_arg_val[2], est_arg_val[3], est_rhs_value); + break; + case OP_FPA_SQRT: + mpf_mngr.sqrt(rm, arg_val[1], rhs_value); + mpf_mngr.sqrt(rm, est_arg_val[1], est_rhs_value); + break; + case OP_FPA_TO_FP: + { + unsigned ebits = rhs->get_decl()->get_parameter(0).get_int(); + unsigned sbits = rhs->get_decl()->get_parameter(1).get_int(); + mpf_mngr.set(rhs_value, ebits, sbits, rm, arg_val[1]); + mpf_mngr.set(est_rhs_value, ebits, sbits, rm, est_arg_val[1]); + break; + } + case OP_FPA_ABS: + { + mpf_mngr.abs(arg_val[0], rhs_value); + mpf_mngr.abs(est_arg_val[0], est_rhs_value); + break; + } + case OP_FPA_MIN: + { + mpf_mngr.minimum( arg_val[1], arg_val[2], rhs_value); + mpf_mngr.minimum( est_arg_val[1], est_arg_val[2], est_rhs_value); + break; + } + case OP_FPA_MAX: + { + mpf_mngr.maximum( arg_val[1], arg_val[2], rhs_value); + mpf_mngr.maximum( est_arg_val[1], est_arg_val[2], est_rhs_value); + break; + } + case OP_FPA_ROUND_TO_INTEGRAL: + { + mpf_mngr.round_to_integral(rm,arg_val[1],rhs_value); + mpf_mngr.round_to_integral(rm,est_arg_val[1],est_rhs_value); + break; + } + + default: + NOT_IMPLEMENTED_YET(); + break; + } + + } + + void evaluate_constant( + app * rhs, + model_ref const & mdl, + mpf_manager & mpf_mngr, + obj_map & actual_value, + mpf & rhs_value, + mpf & est_rhs_value){ + + expr_ref exp(m); + mdl->eval(rhs, exp, true); + m_float_util.is_numeral(exp, rhs_value); //OLD:is_value + + if (actual_value.contains(rhs)) + mpf_mngr.set(est_rhs_value, *actual_value.find(rhs)); + else { + mpf * tmp = alloc(mpf); + mpf_mngr.set(*tmp, rhs_value); + actual_value.insert(rhs, tmp); + mpf_mngr.set(est_rhs_value, rhs_value); + } + } + + void calculate_error( + expr_ref & lhs, + mpf_manager & mpf_mngr, + obj_map & precise_op, + obj_map & err_est, + mpf & lhs_value, + mpf & est_rhs_value, + bool children_have_finite_err){ + mpf err, rel_err; + if (!mpf_mngr.eq(lhs_value, est_rhs_value) && + !(mpf_mngr.is_nan(lhs_value) && mpf_mngr.is_nan(est_rhs_value))) { +#ifdef Z3DEBUG + std::cout << "Increasing precision of " << mk_ismt2_pp(lhs, m) << + " because " << mk_ismt2_pp(lhs, m) << " != " << + mpf_mngr.to_string(est_rhs_value) << std::endl; +#endif + //TODO: smarter adjustment to be implemented + precise_op.insert(lhs, false); + if (mpf_mngr.is_regular(lhs_value) && mpf_mngr.is_regular(est_rhs_value)) { + mpf_mngr.sub(MPF_ROUND_TOWARD_ZERO, est_rhs_value, lhs_value, err); + mpf_mngr.div(MPF_ROUND_TOWARD_ZERO, err, lhs_value, rel_err); + mpf_mngr.abs(rel_err); + } + else// One of the two is a special value; in this case the relative error is +INF + mpf_mngr.mk_pinf(11, 53, rel_err); + + if(children_have_finite_err) + err_est.insert(lhs, mpf_mngr.to_double(rel_err)); + +#ifdef Z3DEBUG + std::cout << "Error estimate: "<size(); j++) { + mdl->eval(g->form(j), res, true); + if (!m.is_true(res)) { + std::cout << "Failed: " << mk_ismt2_pp(g->form(j), m) << std::endl; + std::cout << "Evaluates to: " << mk_ismt2_pp(res, m) << std::endl; + is_model=false; + } + } + return is_model; + } + void evaluate_and_patch( + func_decl_ref_vector const & cnsts, + model_ref const & mdl, + model_ref & full_mdl, + goal_ref const & g, + obj_map & cnst2term_map, + obj_map & err_est) { + + mpf_manager & mpf_mngr = m_float_util.fm(); + expr_ref lhs(m), lhs_eval(m); + app * rhs; + mpf arg_val[4]; //First argument can be rounding mode + mpf est_arg_val[4]; + mpf lhs_value, rhs_value, est_rhs_value; + mpf_rounding_mode rm; + + mpf err, rel_err; + + + + obj_map precise_op; + obj_map actual_value; + while (precise_op.size() != cnst2term_map.size()) + for(unsigned i=0;ieval(lhs, lhs_eval, true); + + if (m_float_util.is_numeral(lhs_eval, lhs_value)) {//OLD:is_value + bool precise_children = true; + bool seen_all_children = true; + bool children_have_finite_err = true; + + obtain_values(rhs, mdl, full_mdl,mpf_mngr,cnst2term_map,precise_op,actual_value, + err_est, rm, precise_children, seen_all_children, children_have_finite_err, arg_val, est_arg_val ); + + + if (seen_all_children) {//If some arguments are not evaluated yet, skip + if (rhs->get_num_args() == 0) + evaluate_constant(rhs,mdl,mpf_mngr,actual_value, rhs_value, est_rhs_value); + else + full_semantics_eval(rhs,mpf_mngr,rm,arg_val,est_arg_val, rhs_value, est_rhs_value); + + if (mpf_mngr.eq(rhs_value, est_rhs_value)) { + full_mdl->register_decl((to_app(lhs))->get_decl(), m_float_util.mk_value(est_rhs_value)); + precise_op.insert(lhs, true); + } + else { + full_mdl->register_decl((to_app(lhs))->get_decl(), m_float_util.mk_value(est_rhs_value)); +#ifdef Z3DEBUG + std::cout << "Assigning " << mk_ismt2_pp(lhs, m) << + " value " << mpf_mngr.to_string(est_rhs_value) << std::endl + << "Values of " << mk_ismt2_pp(lhs, m) << std::endl + << "Precise children: " << ((precise_children) ? "True" : "False") << std::endl + << "Lhs: " << mk_ismt2_pp(lhs_eval, m) << std::endl + << "Model: " << mpf_mngr.to_string(rhs_value) << std::endl + << "Estimate: " << mpf_mngr.to_string(est_rhs_value) << std::endl; +#endif + + calculate_error(lhs,mpf_mngr,precise_op,err_est,lhs_value,est_rhs_value,children_have_finite_err); + + } + if (!actual_value.contains(lhs)) { + mpf * tmp = alloc(mpf); + mpf_mngr.set(*tmp, est_rhs_value); + actual_value.insert(lhs, tmp); + } + + if (!precise_children && !precise_op.contains(lhs)) { + std::cout << mk_ismt2_pp(lhs, m) << " is imprecise because some children are imprecise." << std::endl; + precise_op.insert(lhs, false); + } + } + } + } + + for (obj_map::iterator it = actual_value.begin(); + it != actual_value.end(); + it++) + mpf_mngr.del(*it->m_value); + + mpf_mngr.del(err); + mpf_mngr.del(rel_err); + mpf_mngr.del(lhs_value); + mpf_mngr.del(rhs_value); + mpf_mngr.del(est_rhs_value); + + for (unsigned i = 0; i < 4; i++) { + mpf_mngr.del(arg_val[i]); + mpf_mngr.del(est_arg_val[i]); + } + } + + bool precise_model_reconstruction( + model_ref const & mdl, + model_ref & full_mdl, + goal_ref const & g, + obj_map & err_est,//mpf* + func_decl_ref_vector const & cnsts, + obj_map & cnst2term_map) { +#ifdef Z3DEBUG + std::cout << "Attempting to patch small-float model" << std::endl; +#endif + expr_ref res(m); + bool is_model=true; + + //Evaluation of the model using full fpa semantics and construction of the full model + evaluate_and_patch(cnsts, mdl, full_mdl, g, cnst2term_map, err_est); + +#ifdef Z3DEBUG + std::cout<::iterator it = err_est.begin(); + it!= err_est.end(); it++) { + std::cout<m_key,m)<<":"<m_value<get_num_constants(); j++) { + if (!cnst2term_map.contains(mdl->get_constant(j)) + && !full_mdl->get_const_interp(mdl->get_constant(j))) { + mdl->eval(mdl->get_constant(j), res); + full_mdl->register_decl(mdl->get_constant(j), res); + } + } + + //Evaluate the full model + is_model = evaluate_model(g,full_mdl); + + return is_model; + } + + void calculate_relative_error( + obj_map & err_est, + obj_map & expr_count, + obj_map & err_ratio_map) { + unsigned num_args=0; + expr_ref exp(m); + double out_err,cur,err_ratio, avg_err; + + //AZ: Currently ignoring the expr_count, since it was blocking consideration of some expressions + for (obj_map::iterator it = err_est.begin(); + it != err_est.end(); + it++) { + // if any ancestor node has an error current node will be in expr_count. + /*if (!expr_count.contains(it->m_key)) + continue;*/ + + exp = it->m_key; + out_err = it->m_value; + num_args = to_app(exp)->get_num_args(); + + // Calculate average error of input params + avg_err = 0.0; + if (num_args > 0) { + for (unsigned i=0; iget_arg(i); + if (err_est.contains(arg)) { + cur = err_est.find(arg); + avg_err = avg_err + cur; + } + } + avg_err = avg_err/num_args; + } + // Relative error when input error exists, otherwise just output error + err_ratio = fabs((avg_err != (double) 0)? out_err / avg_err : out_err); + + if(expr_count.contains(exp)) { + if(ERR_OP) + err_ratio *= 1 + expr_count.find(exp); + else + err_ratio += expr_count.find(exp); + } + err_ratio_map.insert(exp, err_ratio); + } + + TRACE("fpa2bv_approx", + tout << "ERROR RATIO MAP: " << std::endl; + for (obj_map::iterator it = err_ratio_map.begin();// mpf* + it != err_ratio_map.end(); + it++) + tout << mk_ismt2_pp(it->m_key, m) << ": " <m_value<< std::endl; ); + + +#ifdef Z3DEBUG + std::cout<<"Error ratio:"<::iterator it = err_ratio_map.begin();//mpf* + it != err_ratio_map.end(); + it++) + std::cout<< mk_ismt2_pp(it->m_key, m) << ": " << it->m_value<< std::endl; + std::cout.flush(); +#endif + + } + + + void rank_terms(obj_map & err_ratio_map, std::list & ranked_terms) + { + unsigned kth = (unsigned)(err_ratio_map.size()*K_PERCENTAGE); + if (kth<10) kth=K_MIN; + SASSERT(!err_ratio_map.empty()); + + //Insertion sort the error ratios, keeping only the k highest elements + obj_map::iterator it = err_ratio_map.begin(); + struct pair * p = new struct pair(); + p->exp=it->m_key; + p->quotient=it->m_value; + ranked_terms.push_front(p); + + for (it++; it != err_ratio_map.end(); it++) { + if (ranked_terms.size()m_value >= ranked_terms.back()->quotient) { + std::list::iterator pos = ranked_terms.begin(); + while (pos!=ranked_terms.end() && it->m_value <= ranked_terms.back()->quotient) + pos++; + struct pair * p = new struct pair(); + p->exp=it->m_key; + p->quotient=it->m_value; + ranked_terms.insert(pos, p); + if (ranked_terms.size() > kth) { + delete ranked_terms.back(); + ranked_terms.pop_back(); + } + } + } + + } + + void increase_precision( + std::list & ranked_terms, + func_decl_ref_vector const & cnsts, + obj_map & cnst2prec_map, + obj_map & cnst2term_map, + obj_map & new_map){ + + //Refine chosen terms and find the any input 'variables' which are + //its immediate arguments and refine them as well +#ifdef Z3DEBUG + std::cout<<"Increasing precision:"<::iterator itp = ranked_terms.begin(); + itp != ranked_terms.end(); + itp++) { + app * cur = to_app((*itp)->exp); + func_decl * f = cur->get_decl(); + unsigned new_prec = PREC_INCREMENT, old_prec; + bool in_new_map; + + if (cnst2prec_map.contains(f)) + new_prec += cnst2prec_map.find(f); + + new_prec= (new_prec > MAX_PRECISION) ? MAX_PRECISION : new_prec; + new_map.insert(f, new_prec); + +#ifdef Z3DEBUG + std::cout << f->get_name() << ":" << new_prec << std::endl; + std::cout << mk_ismt2_pp(cur, m) << ":" << new_prec << std::endl; +#endif + + if(cnst2term_map.contains(f)) + cur = cnst2term_map.find(f); + // Refine constants that are direct arguments of this term + for(unsigned i=0; iget_num_args();i++){ + func_decl * arg_decl = to_app(cur->get_arg(i))->get_decl(); + if (!cnst2term_map.contains(arg_decl) && //Not a constant introduced by flattening + !m_float_util.is_rm(cur->get_arg(i)) && //OLD:is_rm(...,rm) + !m_float_util.is_numeral(cur->get_arg(i))) { //OLD:is_value + //It is an input 'variable' + if ( (in_new_map = new_map.contains(arg_decl))) + old_prec=new_map.find(arg_decl); + else if (cnst2prec_map.contains(arg_decl)) + old_prec = cnst2prec_map.find(arg_decl); + else + old_prec=0; + + if (old_prec < new_prec) { + if (in_new_map) + new_map.remove(arg_decl); + SASSERT(new_prec <= MAX_PRECISION); + new_map.insert(arg_decl, new_prec); + std::cout << " " << arg_decl->get_name() << ":" << new_prec << std::endl; +#ifdef Z3DEBUG + std::cout<<" "<get_arg(i),m)<<":"< & cnst2prec_map, + obj_map & cnst2term_map, + obj_map & err_est, + obj_map & new_map) { + + obj_map err_ratio_map; + obj_map expr_count; + std::list ranked_terms; + + boolean_comparison_of_models(g, mdl, full_mdl, cnst2term_map, expr_count); + calculate_relative_error(err_est, expr_count, err_ratio_map); + if (err_ratio_map.empty()) { + proof_guided_refinement(g,cnsts,cnst2prec_map,new_map); + } + else { + rank_terms (err_ratio_map,ranked_terms); + increase_precision(ranked_terms,cnsts,cnst2prec_map,cnst2term_map,new_map); + } + } + + void simplify(goal_ref mg) { + ast_manager &m = mg->m(); // CMW: <--- We use the manager of the goal, so this works for any manager. + expr_ref new_curr(m); + proof_ref new_pr(m); + + th_rewriter simplifier(m, m_params); + + // CMW: we need to eliminate AND expressions. + params_ref elim_and(m_params); + elim_and.set_bool("elim_and", true); + // elim_and.set_uint("max_depth", 1); // CMW: This number can have a big impact on performance, either way. + simplifier.updt_params(elim_and); + + SASSERT(mg->is_well_sorted()); + TRACE("before_simplifier", mg->display(tout);); + m_num_steps = 0; + if (mg->inconsistent()) + return; + for (unsigned idx = 0; idx < mg->size(); idx++) { + if (mg->inconsistent()) + break; + expr * curr = mg->form(idx); + simplifier(curr, new_curr, new_pr); + m_num_steps += simplifier.get_num_steps(); + if (mg->proofs_enabled()) { + proof * pr = mg->pr(idx); + new_pr = m.mk_modus_ponens(pr, new_pr); + } + mg->update(idx, new_curr, new_pr, mg->dep(idx)); + } + TRACE("after_simplifier_bug", mg->display(tout);); + mg->elim_redundancies(); + TRACE("after_simplifier", mg->display(tout);); + TRACE("after_simplifier_detail", mg->display_with_dependencies(tout);); + SASSERT(mg->is_well_sorted()); + } + + bool fully_encoded(obj_map const & precision_map) + { + for (obj_map::iterator it = precision_map.begin(); + it != precision_map.end(); + it++) + if (it->m_value < MAX_PRECISION) return false; + return true; + } + + void bitblast(goal_ref const & g, + fpa2bv_converter_prec & fpa2bv, + bit_blaster_rewriter & bv2bool, + obj_map & const2prec_map, + sat::solver & solver, + atom2bool_var & map) + { + // CMW: This is all done using the temporary manager! + expr_ref new_curr(*m_temp_manager); + proof_ref new_pr(*m_temp_manager); + std::cout.flush(); + + SASSERT(g->is_well_sorted()); + + simplify(g); + + //fpa2bv + fpa2bv_rewriter_prec fpa2bv_rw(*m_temp_manager, fpa2bv, m_params); + fpa2bv_rw.m_cfg.set_mappings(&const2prec_map); + m_num_steps = 0; + unsigned size = g->size(); + for (unsigned idx = 0; idx < size; idx++) { + if (g->inconsistent()) + break; + expr * curr = g->form(idx); +#ifdef Z3DEBUG + std::cout<pr(idx); + new_pr = m_temp_manager->mk_modus_ponens(pr, new_pr); + } + g->update(idx, new_curr, new_pr, g->dep(idx)); + + SASSERT(g->is_well_sorted()); + } + + //Adding the equalities that fix bits + for(unsigned i=0;iassert_expr(fpa2bv.m_extra_assertions.get(i)); + + SASSERT(g->is_well_sorted()); + + simplify(g); + + //Bitblasting + TRACE("before_bit_blaster", g->display(tout);); + m_num_steps = 0; + size = g->size(); + for (unsigned idx = 0; idx < size; idx++) { + if (g->inconsistent()) + break; + expr * curr = g->form(idx); + bv2bool(curr, new_curr, new_pr); + m_num_steps += bv2bool.get_num_steps(); + if (m_proofs_enabled) { + proof * pr = g->pr(idx); + new_pr = m_temp_manager->mk_modus_ponens(pr, new_pr); + } + g->update(idx, new_curr, new_pr, g->dep(idx)); + } + + g->inc_depth(); + + simplify(g); + + TRACE("before_sat_solver", g->display(tout);); + g->elim_redundancies(); + + goal2sat::dep2asm_map d2am ; + m_goal2sat(*g, m_params, solver, map, d2am , false); + + + TRACE("sat_solver_unknown", tout << "interpreted_atoms: " << map.interpreted_atoms() << "\n"; + atom2bool_var::iterator it = map.begin(); + atom2bool_var::iterator end = map.end(); + for (; it != end; ++it) { + if (!is_uninterp_const(it->m_key)) + tout << mk_ismt2_pp(it->m_key, *m_temp_manager) << "\n"; + }); + + CASSERT("sat_solver", solver.check_invariant()); + IF_VERBOSE(TACTIC_VERBOSITY_LVL, solver.display_status(verbose_stream());); + TRACE("sat_dimacs", solver.display_dimacs(tout);); + } + + model_ref get_fpa_model(goal_ref const & g, + fpa2bv_converter_prec & fpa2bv, + bit_blaster_rewriter & bv2bool, + sat::solver & solver, + atom2bool_var & map) { + // CMW: This is all done using the temporary manager, until at the very end we translate the model back to this->m. + model_ref md = alloc(model, *m_temp_manager); + sat::model const & ll_m = solver.get_model(); + TRACE("sat_tactic", for (unsigned i = 0; i < ll_m.size(); i++) + tout << i << ":" << ll_m[i] << " "; tout << "\n";); + atom2bool_var::iterator it = map.begin(); + atom2bool_var::iterator end = map.end(); + for (; it != end; ++it) { + expr * n = it->m_key; + sat::bool_var v = it->m_value; + if (is_app(n) && to_app(n)->get_decl()->get_arity() != 0) + continue; + TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, *m_temp_manager) << "\nvar: " << v << "\n";); + switch (sat::value_at(v, ll_m)) { + case l_true: md->register_decl(to_app(n)->get_decl(), m_temp_manager->mk_true()); break; + case l_false: md->register_decl(to_app(n)->get_decl(), m_temp_manager->mk_false()); break; + default: + break; + } + } + + TRACE("sat_tactic", model_v2_pp(tout, *md);); + model_converter_ref bb_mc = mk_bit_blaster_model_converter(*m_temp_manager, bv2bool.const2bits()); + + model_converter_ref bv_mc = mk_fpa2bv_model_converter(*m_temp_manager, fpa2bv.const2bv(), fpa2bv.rm_const2bv(), fpa2bv.uf2bvuf(), fpa2bv.uf23bvuf()); + bb_mc->operator()(md, 0); + bv_mc->operator()(md, 0); + +#ifdef Z3DEBUG + std::cout << "Model: " << std::endl; + for (unsigned i = 0 ; i < md->get_num_constants(); i++) { + func_decl * d = md->get_constant(i); + std::cout << d->get_name() << " = " << mk_ismt2_pp(md->get_const_interp(d), *m_temp_manager) << std::endl; + } +#endif + // md is in terms of the temporary manager. + ast_translation translator(*m_temp_manager, this->m); + return md->translate(translator); + } + + void encode_fpa_terms( goal_ref const & g, + obj_map & const2term_map) + { + for (obj_map::iterator it = const2term_map.begin(); + it!=const2term_map.end(); + it++) { + expr_ref q(m); +#ifdef Z3DEBUG + std::cout << "Adding " << it->m_key->get_name() << " = " << mk_ismt2_pp(it->m_value, m) << std::endl; +#endif + q = m.mk_eq(m.mk_const(it->m_key), it->m_value); + g->assert_expr(q); + } + } + + lbool approximate_model_construction(goal_ref & g, obj_map & const2prec_map) { + lbool r = l_undef; + // CMW: The following will introduce lots of stuff that we don't need (e.g., symbols) + // To save memory, we use a separate, new manager that we can throw away afterwards. + m_temp_manager = alloc(ast_manager, PGM_DISABLED); + { + ast_translation translator(m, *m_temp_manager); + goal_ref ng = g->translate(translator); + obj_map const2prec_map_tm; + + for (obj_map::iterator it = const2prec_map.begin(); + it!=const2prec_map.end(); + it++) + const2prec_map_tm.insert(translator(it->m_key), it->m_value); + + sat::solver sat_solver(m_params, 0); + atom2bool_var atom_map(*m_temp_manager); + { tactic_report report_i("fpa2bv_approx_before_bitblaster", *ng); } + fpa2bv_converter_prec fpa2bv(*m_temp_manager, m_mode); + bit_blaster_rewriter bv2bool(*m_temp_manager, m_params); + bitblast(ng, fpa2bv, bv2bool, const2prec_map_tm, sat_solver, atom_map); + { tactic_report report_i("fpa2bv_approx_after_bitblaster", *ng); } + + std::cout << "Iteration variables: " << sat_solver.num_vars() << std::endl; + std::cout << "Iteration clauses: " << sat_solver.num_clauses() << std::endl; + r = sat_solver.check(); + + if (r == l_true) + { + // we need to get the model and translate it back to m. + m_fpa_model = get_fpa_model(ng, fpa2bv, bv2bool, sat_solver, atom_map).get(); + } + else + m_fpa_model = 0; + + // CMW: translator, etc, gets destroyed here, so all references + // to temporary expressions are gone. + } + + dealloc(m_temp_manager); + m_temp_manager = 0; + + return r; + } + + void lift( goal_ref const & g, func_decl_ref_vector & constants, obj_map * const2term_map ) + { + expr_ref new_new_curr(m); + expr_ref new_curr(m); + proof_ref new_pr(m); + + simplify(g); + + //Renaming subexpressions using new constants + const_intro_rewriter const_rewriter(m, m_params); + for (unsigned idx = 0; idx < g->size(); idx++) { + if (g->inconsistent()) + break; + expr * curr = g->form(idx); + const_rewriter(curr, new_curr, new_pr); //Introduces constants that replace subexpressions + m_num_steps += const_rewriter.get_num_steps(); + if (m_proofs_enabled) { + proof * pr = g->pr(idx); + new_pr = m.mk_modus_ponens(pr, new_pr); + } + g->update(idx, new_curr, new_pr, g->dep(idx)); + } + + + constants.set(const_rewriter.m_cfg.m_introduced_consts); + const2term_map->swap(const_rewriter.m_cfg.m_const2term_map); + + // Note: Ideally, we would directly encode them. For now we're lazy and just add equalities + // and we rely on fpa2bv_converter_prec to `magically' recognize the equalities we added. + { tactic_report report_i("fpa2bv_approx_before_fpa_terms", *(g.get())); } + encode_fpa_terms(g, *const2term_map); + SASSERT(g.get()->is_well_sorted()); + + + } + + void verify_precise_model( goal_ref const & g, + model_ref & full_mdl, + func_decl_ref_vector & constants, + obj_map & const2term_map, + model_converter_ref & mc, + goal_ref_buffer & result ){ + expr_ref res(m); + + for (unsigned j = 0; j < g->size(); j++) { + full_mdl->eval(g->form(j), res, true); + if (!m.is_true(res)) { + std::cout << "Failed: " << mk_ismt2_pp(g->form(j), m) << std::endl; + std::cout << "Evaluates to: " << mk_ismt2_pp(res, m) << std::endl; + } + SASSERT(m.is_true(res)); + } + + std::cout << "Full model: " << std::endl; + for (unsigned i = 0 ; i < full_mdl->get_num_decls(); i++) + { + func_decl * d = full_mdl->get_decl(i); + if(constants.contains(d)) + std::cout << d->get_name() << " = " << mk_ismt2_pp(full_mdl->get_const_interp(d), m) << std::endl; + } + std::cout.flush(); + + result.back()->reset(); + + // Filter all the constants we introduced earlier from the model. + filter_model_converter * fmc = alloc(filter_model_converter, m); + for (obj_map::iterator it = const2term_map.begin(); + it != const2term_map.end(); + it++) + fmc->insert(it->m_key); + mc = concat(fmc, model2model_converter(m_fpa_model.get())); + } + + void setup_options(goal_ref const & g){ + SASSERT(g->is_well_sorted()); + fail_if_proof_generation("fpa2bv_approx", g); + fail_if_unsat_core_generation("fpa2bv_approx", g); + m_proofs_enabled = g->proofs_enabled(); + m_produce_models = g->models_enabled(); + m_produce_unsat_cores = g->unsat_core_enabled(); + m_num_steps = 0; + } + + + + void print_constants(func_decl_ref_vector & constants, obj_map & const2prec_map){ +#ifdef Z3DEBUG + for(unsigned i=0;iget_name()<<":"< const2prec_map; + obj_map next_const2prec_map; + func_decl_ref_vector constants(m); + obj_map const2term_map; + lbool r = l_true; + unsigned iteration_cnt = 0; + stopwatch sw; + + tactic_report report("fpa2bv_approx", *g); + TRACE("fpa2bv_approx", tout << "BEFORE: " << std::endl; g->display(tout);); + result.reset(); + result.push_back(g.get()); + + SASSERT(g->is_well_sorted()); + if (g->inconsistent()) + return; + + lift(g, constants, &const2term_map); + + init_precision_mapping(constants, const2prec_map, const2term_map); + + std::cout << "Simplified goal:" << std::endl; + g->display(std::cout); + + while (!solved && !m_cancel) + { + std::cout << "=============== Starting iteration " << ++iteration_cnt << std::endl; + + sw.reset(); + sw.start(); + + // Copy the goal + goal_ref mg(alloc(goal, g->m(),g->proofs_enabled(),g->models_enabled(),g->unsat_core_enabled())); + mg->copy_from(*g.get()); + tactic_report report_i("fpa2bv_approx_i", *mg); + + print_constants(constants, const2prec_map); + + TRACE("fpa2bv_approx_goal_i", mg->display(tout); ); + + r = approximate_model_construction(mg, const2prec_map); + + std::cout << "Approximation is " << (r==l_true?"SAT":r==l_false?"UNSAT":"UNKNOWN") << std::endl; + + if (r == l_true) { + model_ref full_mdl = alloc(model, m); + obj_map err_est; + + if (fully_encoded(const2prec_map)) { + full_mdl = m_fpa_model; + solved = true; + std::cout<<"Model is at full precision, no patching needed!"< This is unsat. + solved = true; + result.back()->reset(); + result.back()->assert_expr(m.mk_false()); + } + } else { + // CMW: When the sat solver comes back with `unknown', what shall we do? + // AZ: Blindly refine? + m_cancel = true; + } + + const2prec_map.swap(next_const2prec_map); + next_const2prec_map.reset(); + std::cout << "Iteration time: " << sw.get_current_seconds() << std::endl; + } + + std::cout << "=============== Terminating " << std::endl; + dec_ref_map_key_values(m, const2term_map); + std::cout << "Iteration count: " << iteration_cnt << std::endl; + } + }; + + imp * m_imp; + params_ref m_params; + +public: + fpa2bv_approx_tactic(ast_manager & m, params_ref const & p) : + m_params(p){ + m_imp = alloc(imp, m, p, FPAA_DEFAULT_MODE); + } + + virtual tactic * translate(ast_manager & m) { + return alloc(fpa2bv_approx_tactic, m, m_params); + } + + virtual ~fpa2bv_approx_tactic() { + dealloc(m_imp); + } + + virtual void updt_params(params_ref const & p) { + m_params = p; + m_imp->updt_params(p); + } + + virtual void collect_param_descrs(param_descrs & r) { + } + + virtual void operator()(goal_ref const & in, goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, + expr_dependency_ref & core) { + (*m_imp)(in, result, mc, pc, core); + } + + virtual void cleanup() { + ast_manager & m = m_imp->m; + imp * d = m_imp; +#pragma omp critical (tactic_cancel) + { + d = m_imp; + } + dealloc(d); + d = alloc(imp, m, m_params, FPAA_DEFAULT_MODE); +#pragma omp critical (tactic_cancel) + { + m_imp = d; + } + } + +protected: + virtual void set_cancel(bool f) { + if (m_imp) + m_imp->set_cancel(f); +} +}; + +tactic * mk_fpa2bv_approx_tactic(ast_manager & m, params_ref const & p) { + return and_then(clean(alloc(fpa2bv_approx_tactic, m, p)), mk_fail_if_undecided_tactic()); +} + + diff --git a/src/tactic/fpa/fpa2bv_approx_tactic.h b/src/tactic/fpa/fpa2bv_approx_tactic.h new file mode 100644 index 000000000..8838ca99d --- /dev/null +++ b/src/tactic/fpa/fpa2bv_approx_tactic.h @@ -0,0 +1,33 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + fpa2bv_lazy_tactic.h + +Abstract: + + Tactic that converts floating points to bit-vectors lazily + +Author: + + Aleksander Zeljic 2012-11-15 + +Notes: + +--*/ +#ifndef _FPA2BV_APPROX_TACTIC_ +#define _FPA2BV_APPROX_TACTIC_ + +#include"params.h" +class ast_manager; +class tactic; + + + +tactic * mk_fpa2bv_approx_tactic(ast_manager & m, params_ref const & p = params_ref()); +/* + ADD_TACTIC("fpa2bv_approx", "An iterative approximation based bit-blasting decision procedure for FPA.", "mk_fpa2bv_approx_tactic(m, p)") +*/ + +#endif diff --git a/src/tactic/fpa/fpa2bv_converter_prec.cpp b/src/tactic/fpa/fpa2bv_converter_prec.cpp new file mode 100644 index 000000000..046511199 --- /dev/null +++ b/src/tactic/fpa/fpa2bv_converter_prec.cpp @@ -0,0 +1,1594 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + fpa2bv_converter_prec.cpp + +Abstract: + + Conversion routines for Floating Point -> Bit-Vector + +Author: + + Christoph (cwinter) 2012-02-09 + +Notes: + +--*/ +#include"ast_smt2_pp.h" +#include"well_sorted.h" +#include + +#include"fpa2bv_converter_prec.h" + +#define BVULT(X,Y,R) { expr_ref bvult_eq(m), bvult_not(m); m_simp.mk_eq(X, Y, bvult_eq); m_simp.mk_not(bvult_eq, bvult_not); expr_ref t(m); t = m_bv_util.mk_ule(X,Y); m_simp.mk_and(t, bvult_not, R); } +#define BVSLT(X,Y,R) { expr_ref bvslt_eq(m), bvslt_not(m); m_simp.mk_eq(X, Y, bvslt_eq); m_simp.mk_not(bvslt_eq, bvslt_not); expr_ref t(m); t = m_bv_util.mk_sle(X,Y); m_simp.mk_and(t, bvslt_not, R); } + +#define MIN_EBITS 3 +#define MIN_SBITS 3 + +fpa2bv_converter_prec::fpa2bv_converter_prec(ast_manager & m, fpa_approximation_mode mode) : + fpa2bv_converter(m), + m_mode(mode) { +} + +void fpa2bv_converter_prec::fix_bits(unsigned prec, expr_ref rounded, unsigned sbits, unsigned ebits)//expr_ref& fixed, +{ //AZ: TODO: revise! minimal number of legal bits is 3!!!! Remove magic numbers + unsigned szeroes = (unsigned) ((sbits-2)*(MAX_PRECISION - prec +0.0) / MAX_PRECISION);//3 bits are minimum for the significand + unsigned ezeroes = (unsigned) ((ebits - 2)*(MAX_PRECISION - prec + 0.0) / MAX_PRECISION);//2 bits are minimum for the exponent + + expr_ref fix_sig(m), fix_exp(m); + expr * sgn, *sig, *expn; + split_fp( rounded.get(), sgn,sig,expn); + if(ezeroes>0) { + fix_exp=m.mk_eq(m_bv_util.mk_extract(ebits-2, ebits - ezeroes -1, sig), m_bv_util.mk_numeral(0,ezeroes)); + m_extra_assertions.push_back(fix_exp); + SASSERT(is_well_sorted(m, fix_exp)); + } + + if(szeroes>0) { + fix_sig=m.mk_eq(m_bv_util.mk_extract(sbits-2, sbits - szeroes -1, sig), m_bv_util.mk_numeral(0,szeroes)); + m_extra_assertions.push_back(fix_sig); + SASSERT(is_well_sorted(m, fix_sig)); + } +} + +void fpa2bv_converter_prec::mk_const(func_decl * f, unsigned prec, expr_ref & result) { + switch (m_mode) { + case FPAA_SMALL_FLOATS: + { + if (m_const2bv.contains(f)) + result = m_const2bv.find(f); + else { + if (prec == MAX_PRECISION) + fpa2bv_converter::mk_const(f, result); + else { + unsigned ebits = fu().get_ebits(f->get_range()); + unsigned sbits = fu().get_sbits(f->get_range()); + double rel = prec/(double)MAX_PRECISION; + unsigned new_ebits = (unsigned) (rel * (double)ebits); + unsigned new_sbits = (unsigned) (rel * (double)sbits); + if (new_ebits < MIN_EBITS) new_ebits = MIN_EBITS; + if (new_sbits < MIN_SBITS) new_sbits = MIN_SBITS; + sort_ref ns(m), fp_srt(m); + ns = fu().mk_float_sort(new_ebits, new_sbits); + app_ref small_const(m); + small_const = m.mk_fresh_const("small_const", ns); + + fp_srt = fu().mk_float_sort(ebits, sbits); + symbol name("asFloat"); + sort_ref rm_sort(m); + rm_sort = fu().mk_rm_sort(); + //sort * domain[2] = { rm_sort, ns }; + //parameter parameters[2] = { parameter(ebits), parameter(sbits) }; + + fpa2bv_converter::mk_const(small_const->get_decl(), result); + m_const2bv.insert(f, result.get()); + m.inc_ref(f); + m.inc_ref(result.get()); +#ifdef Z3DEBUG + std::cout << f->get_name() << " := " << small_const->get_decl()->get_name() << + " [" << new_sbits<<","<get_name() << " := " << mk_ismt2_pp(result, m) << std::endl;); + } + } + break; + } + case FPAA_PRECISE: + case FPAA_FIXBITS: + fpa2bv_converter::mk_const(f, result); break; + default: + UNREACHABLE(); + break; + } +} +void fpa2bv_converter_prec::mk_small_op(func_decl * f, unsigned new_ebits, unsigned new_sbits, unsigned num, expr * const * args, func_decl_ref & small_op, expr_ref_vector & cast_args) +{ + if (new_ebits < MIN_EBITS) new_ebits = MIN_EBITS; + if (new_sbits < MIN_SBITS) new_sbits = MIN_SBITS; + sort_ref new_srt(m), rm_srt(m); + new_srt = fu().mk_float_sort(new_ebits, new_sbits); + rm_srt = fu().mk_rm_sort(); + + sort_ref_vector new_domain(m); + cast_args.reset(); + + for (unsigned i=0; i < num; i++) // Recreate the domain by replacing the full fpa sort with the smaller one. + { + sort * d_i = f->get_domain(i); + expr * a_i = args[i]; + + if (fu().is_rm(d_i)) + { + new_domain.push_back(rm_srt); + cast_args.push_back(a_i); + } + else if (fu().is_float(f->get_domain(i))) + { + sort * a_i_srt = to_app(a_i)->get_decl()->get_range(); + new_domain.push_back(new_srt); + + // Cast the corresponding argument to the right fpa size + if (is_app(a_i)) + { + if (a_i_srt == new_srt) + cast_args.push_back(a_i); + else { + app_ref rtz(m); + rtz = fu().mk_round_toward_zero(); + expr_ref rm(m); + fpa2bv_converter::mk_rounding_mode(rtz->get_decl(), rm); + + sort * d[2] = { rm_srt, a_i_srt }; + symbol name("asFloat"); + func_decl_ref fd(m); + fd = m.mk_func_decl(name, 2, d, new_srt, func_decl_info(fu().get_family_id(), OP_FPA_TO_FP, new_srt->get_num_parameters(), new_srt->get_parameters())); + + expr_ref t(m); + expr * args[2] = { rm, a_i }; + fpa2bv_converter::mk_to_fp(fd, 2, args, t); + cast_args.push_back(t); + SASSERT(is_well_sorted(m, t)); + } + } + else + NOT_IMPLEMENTED_YET(); + } + else + // just keep everything else + cast_args.push_back(a_i); + } + + parameter parameters[2] = { parameter(new_ebits), parameter(new_sbits) }; + + // May I reuse parts of the existing declaration? CMW: Sure. + + small_op = m.mk_func_decl(f->get_name(), num, new_domain.c_ptr(), new_srt, + func_decl_info(fu().get_family_id(), f->get_decl_kind(), 2, parameters)); + + //std::cout<get_name()<<"["<get_range()); + unsigned sbits = fu().get_sbits(f->get_range()); + double rel = prec/(double)MAX_PRECISION; + unsigned new_ebits = (unsigned) (rel * (double)ebits);// (unsigned) (MIN_EBITS + rel * (double)(ebits-MIN_EBITS)) + unsigned new_sbits = (unsigned) (rel * (double)sbits);// (unsigned) (MIN_SBITS + rel * (double)(sbits-MIN_SBITS)) + mk_small_op(f,new_ebits,new_sbits,num,args, small_op, cast_args); +} + +void fpa2bv_converter_prec::mk_cast_small_to_big(func_decl * f, expr * arg, expr_ref & result) { + unsigned ebits = fu().get_ebits(f->get_range()); + unsigned sbits = fu().get_sbits(f->get_range()); + + app_ref rtz(m); + rtz = fu().mk_round_toward_zero(); + expr_ref rm(m); + fpa2bv_converter::mk_rounding_mode(rtz->get_decl(), rm); + sort_ref rm_srt(m); + rm_srt = fu().mk_rm_sort(); + sort * d[2] = { rm_srt, to_app(arg)->get_decl()->get_range() }; + parameter parameters[2] = { parameter(ebits), parameter(sbits) }; + symbol name("asFloat"); + func_decl_ref cast_up(m); + cast_up = m.mk_func_decl(name, 2, d, f->get_range(), func_decl_info(fu().get_family_id(), OP_FPA_TO_FP, f->get_range()->get_num_parameters(), parameters)); + expr * args[2] = { rm, arg }; + fpa2bv_converter::mk_to_fp(cast_up, 2, args, result); +} + +void fpa2bv_converter_prec::mk_cast_small_to_big(unsigned sbits, unsigned ebits, expr * arg, expr_ref & result) { + app_ref rtz(m); + rtz = fu().mk_round_toward_zero(); + expr_ref rm(m); + fpa2bv_converter::mk_rounding_mode(rtz->get_decl(), rm); + sort_ref rm_srt(m); + rm_srt = fu().mk_rm_sort(); + sort * d[2] = { rm_srt, to_app(arg)->get_decl()->get_range() }; + parameter parameters[2] = { parameter(ebits), parameter(sbits) }; + symbol name("asFloat"); + func_decl_ref cast_up(m); + sort_ref ns(m); + ns = fu().mk_float_sort(ebits, sbits); + cast_up = m.mk_func_decl(name, 2, d, ns, func_decl_info(fu().get_family_id(), OP_FPA_TO_FP, 2, parameters)); + expr * args[2] = { rm, arg }; + fpa2bv_converter::mk_to_fp(cast_up, 2, args, result); +} + + +void fpa2bv_converter_prec::match_sorts(expr * a, expr * b, expr_ref & n_a, expr_ref & n_b) +{ + //Check if the sorts of lhs and rhs match, otherwise cast them to appropriate size? + 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)); + func_decl * a_decl = to_app(a)->get_decl(); + func_decl * b_decl = to_app(b)->get_decl(); + + unsigned a_ebits = fu().get_ebits(a_decl->get_range()); + unsigned a_sbits = fu().get_sbits(a_decl->get_range()); + unsigned b_ebits = fu().get_ebits(b_decl->get_range()); + unsigned b_sbits = fu().get_sbits(b_decl->get_range()); + unsigned n_ebits, n_sbits; + + //if( (a_ebits == b_ebits) && (a_sbits == b_sbits)) + //{//No need for adjustment + n_a = a; + n_b = b; + //} + //else + //{ + if ((a_ebits <= b_ebits) && (a_sbits<= b_sbits)) + {//sort of b is wider than sort of a, we cast a to the sort of b. + mk_cast_small_to_big(b_sbits,b_ebits,a,n_a); + n_b = b; + } + else if ((a_ebits >= b_ebits) && (a_sbits >= b_sbits)) + { + n_a = a; + mk_cast_small_to_big(a_sbits,a_ebits,b,n_b); + } + else + { + n_ebits = (a_ebits < b_ebits)? b_ebits:a_ebits; + n_sbits = (a_sbits < b_sbits)? b_sbits:a_sbits; + mk_cast_small_to_big(n_sbits,n_ebits,a,n_a); + mk_cast_small_to_big(n_sbits,n_ebits,b,n_b); + } + //} +} +void fpa2bv_converter_prec::mk_eq(expr * a, expr * b, expr_ref & result) { + // This is structural equality, not floating point equality. + expr_ref na(m),nb(m); + match_sorts(a,b,na,nb); + fpa2bv_converter::mk_eq(na,nb,result); +} + +void fpa2bv_converter_prec::mk_ite(expr * c, expr * t, expr * f, expr_ref & result) { + expr_ref nt(m),nf(m); + match_sorts(t,f,nt,nf); + fpa2bv_converter::mk_ite(c,nt,nf,result); +} + + + +void fpa2bv_converter_prec::mk_add(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + // AZ: Switch can be moved just before the call to the fix_bits method, everything else should be the same + switch (m_mode) { + case FPAA_PRECISE: + fpa2bv_converter::mk_add(f,num,args,result); + break; + case FPAA_SMALL_FLOATS: + { + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + mk_small_op(f, prec, num, args, small_fd, small_args); + //expr_ref small_op(m); + fpa2bv_converter::mk_add(small_fd, num, small_args.c_ptr(), result); + //mk_cast_small_to_big(f, small_op, result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_add", tout << "small_fd: " << mk_ismt2_pp(small_fd, m) << std::endl << + "result = " << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + + case FPAA_FIXBITS: { + if (MAX_PRECISION == prec) + fpa2bv_converter::mk_add(f,num,args,result); + else{ + //Alternative encoding + /*func_decl * nf = & func_decl(f->get_name(), + f->get_arity(), + f->get_domain(), + f->get_range(), + f->get_info());*/ + + + SASSERT(num == 3); + + expr_ref rm(m), x(m), y(m); + rm = args[0]; + x = args[1]; + y = args[2]; + + expr_ref nan(m), nzero(m), pzero(m); + mk_nan(f, nan); + mk_nzero(f, nzero); + mk_pzero(f, pzero); + + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); + expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); + mk_is_nan(x, x_is_nan); + mk_is_zero(x, x_is_zero); + mk_is_pos(x, x_is_pos); + mk_is_neg(x, x_is_neg); + mk_is_inf(x, x_is_inf); + mk_is_nan(y, y_is_nan); + mk_is_zero(y, y_is_zero); + mk_is_pos(y, y_is_pos); + mk_is_neg(y, y_is_neg); + mk_is_inf(y, y_is_inf); + + dbg_decouple("fpa2bv_add_x_is_nan", x_is_nan); + dbg_decouple("fpa2bv_add_x_is_zero", x_is_zero); + dbg_decouple("fpa2bv_add_x_is_pos", x_is_pos); + dbg_decouple("fpa2bv_add_x_is_neg", x_is_neg); + dbg_decouple("fpa2bv_add_x_is_inf", x_is_inf); + dbg_decouple("fpa2bv_add_y_is_nan", y_is_nan); + dbg_decouple("fpa2bv_add_y_is_zero", y_is_zero); + dbg_decouple("fpa2bv_add_y_is_pos", y_is_pos); + dbg_decouple("fpa2bv_add_y_is_neg", y_is_neg); + dbg_decouple("fpa2bv_add_y_is_inf", y_is_inf); + + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); + + m_simp.mk_or(x_is_nan, y_is_nan, c1); + v1 = nan; + + mk_is_inf(x, c2); + expr_ref nx(m), ny(m), nx_xor_ny(m), inf_xor(m); + mk_is_neg(x, nx); + mk_is_neg(y, ny); + m_simp.mk_xor(nx, ny, nx_xor_ny); + m_simp.mk_and(y_is_inf, nx_xor_ny, inf_xor); + mk_ite(inf_xor, nan, x, v2); + + mk_is_inf(y, c3); + expr_ref xy_is_neg(m), v3_and(m); + m_simp.mk_xor(x_is_neg, y_is_neg, xy_is_neg); + m_simp.mk_and(x_is_inf, xy_is_neg, v3_and); + mk_ite(v3_and, nan, y, v3); + + expr_ref rm_is_to_neg(m), signs_and(m), signs_xor(m), v4_and(m), rm_and_xor(m), neg_cond(m); + m_simp.mk_and(x_is_zero, y_is_zero, c4); + m_simp.mk_and(x_is_neg, y_is_neg, signs_and); + m_simp.mk_xor(x_is_neg, y_is_neg, signs_xor); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + m_simp.mk_and(rm_is_to_neg, signs_xor, rm_and_xor); + m_simp.mk_or(signs_and, rm_and_xor, neg_cond); + mk_ite(neg_cond, nzero, pzero, v4); + m_simp.mk_and(x_is_neg, y_is_neg, v4_and); + mk_ite(v4_and, x, v4, v4); + + c5 = x_is_zero; + v5 = y; + + c6 = y_is_zero; + v6 = x; + + // Actual addition. + 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), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, false); + unpack(y, b_sgn, b_sig, b_exp, b_lz, false); + + dbg_decouple("fpa2bv_add_unpack_a_sgn", a_sgn); + dbg_decouple("fpa2bv_add_unpack_a_sig", a_sig); + dbg_decouple("fpa2bv_add_unpack_a_exp", a_exp); + dbg_decouple("fpa2bv_add_unpack_b_sgn", b_sgn); + dbg_decouple("fpa2bv_add_unpack_b_sig", b_sig); + dbg_decouple("fpa2bv_add_unpack_b_exp", b_exp); + + expr_ref swap_cond(m); + swap_cond = m_bv_util.mk_sle(a_exp, b_exp); + + expr_ref c_sgn(m), c_sig(m), c_exp(m), d_sgn(m), d_sig(m), d_exp(m); + m_simp.mk_ite(swap_cond, b_sgn, a_sgn, c_sgn); + m_simp.mk_ite(swap_cond, b_sig, a_sig, c_sig); // has sbits + m_simp.mk_ite(swap_cond, b_exp, a_exp, c_exp); // has ebits + m_simp.mk_ite(swap_cond, a_sgn, b_sgn, d_sgn); + m_simp.mk_ite(swap_cond, a_sig, b_sig, d_sig); // has sbits + m_simp.mk_ite(swap_cond, a_exp, b_exp, d_exp); // has ebits + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + add_core(sbits, ebits, rm, + c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, + res_sgn, res_sig, res_exp); + + //Add the fixed zeroes here...? + expr_ref sbits_zero(m), ebits_zero(m); + // std::cout<<"res_sgn: "<1) + // //Exponent = 1 bit for bias, fixed bits, actual exponent bits + // res_exp=m_bv_util.mk_concat(m_bv_util.mk_extract(ebits+1, ebits-1, res_exp), + // m_bv_util.mk_concat(m_bv_util.mk_numeral(0,ezeroes), + // m_bv_util.mk_extract(ebits - 2 - ezeroes, 0 ,res_exp))); + // if(sones>1) + // res_sig=m_bv_util.mk_concat(m_bv_util.mk_extract(sbits+3,sones+4,res_sig), + // m_bv_util.mk_concat(m_bv_util.mk_numeral(0,sones), + // m_bv_util.mk_extract(3,0,res_sig))); + // + // std::cout<<"res_sgn': "<get_range(), rm, res_sgn, res_sig, res_exp, rounded); + + + mk_ite(is_zero_sig, zero_case, rounded, v7);*/ + + + + expr_ref rounded(m);//, fixed(m); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); + + fix_bits(prec, rounded, sbits,ebits); + mk_ite(is_zero_sig, zero_case, rounded, v7); + + // signed sones=((sbits-3)*(MAX_PRECISION - prec +0.0)/MAX_PRECISION);//3 bits are minimum for the significand + // unsigned ezeroes=((ebits-2)*(MAX_PRECISION - prec+0.0)/MAX_PRECISION);//2 bits are minimum for the exponent + // expr_ref fix_sig(m), fix_exp(m), fix_sgn(m), rnd_sig(m), rnd_exp(m), rnd_sgn(m), rnd_lz(m); + // expr * sgn, *sig, *expn; + // split( rounded.get(), sgn,sig,expn); + // + // + // if(ezeroes>1) + // //Exponent = 1 bit for bias, fixed bits, actual exponent bits + // fix_exp=m_bv_util.mk_concat(m_bv_util.mk_extract(ebits-1, ebits-1, expn), + // m_bv_util.mk_concat(m_bv_util.mk_numeral(0,ezeroes), + // m_bv_util.mk_extract(ebits - 2 - ezeroes, 0 , expn))); + // if(sones>1) + // fix_sig=m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-2,sones,sig), + // m_bv_util.mk_numeral(0,sones)); + // + // mk_triple(sgn, fix_sig, fix_exp, fixed); + // SASSERT(is_well_sorted(m, fixed)); + //mk_ite(is_zero_sig, zero_case, rounded, v7); + + + + + mk_ite(c6, v6, v7, result); + mk_ite(c5, v5, result, 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); + + SASSERT(is_well_sorted(m, result)); + + TRACE("fpa2bv_add", tout << "ADD = " << mk_ismt2_pp(result, m) << std::endl; ); + } + break; + } + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_sub(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 3); + expr_ref t(m); + fpa2bv_converter::mk_neg(f, 1, &args[2], t); + expr * nargs[3] = { args[0], args[1], t }; + + switch (m_mode) { + case FPAA_PRECISE: + fpa2bv_converter::mk_add(f, 3, nargs, result); + break; + case FPAA_FIXBITS: // Call the add with prec + case FPAA_SMALL_FLOATS: + fpa2bv_converter_prec::mk_add(f, prec, 3, nargs, result); + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_uminus(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_PRECISE: + case FPAA_SMALL_FLOATS: + case FPAA_FIXBITS: // for now, encode fully. + fpa2bv_converter::mk_neg(f,num,args,result); + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_mul(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_PRECISE: + fpa2bv_converter::mk_mul(f,num,args,result); + break; + case FPAA_SMALL_FLOATS: + { + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + mk_small_op(f, prec, num, args, small_fd, small_args); + expr_ref small_op(m); + fpa2bv_converter::mk_mul(small_fd, num, small_args.c_ptr(), small_op); + mk_cast_small_to_big(f, small_op, result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_mul", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + case FPAA_FIXBITS: // for now, encode fully. + { SASSERT(num == 3); + + expr_ref rm(m), x(m), y(m); + rm = args[0]; + x = args[1]; + y = args[2]; + + 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_ninf(f, ninf); + mk_pinf(f, pinf); + + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); + expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); + mk_is_nan(x, x_is_nan); + mk_is_zero(x, x_is_zero); + mk_is_pos(x, x_is_pos); + mk_is_inf(x, x_is_inf); + mk_is_nan(y, y_is_nan); + mk_is_zero(y, y_is_zero); + mk_is_pos(y, y_is_pos); + mk_is_inf(y, y_is_inf); + + dbg_decouple("fpa2bv_mul_x_is_nan", x_is_nan); + dbg_decouple("fpa2bv_mul_x_is_zero", x_is_zero); + dbg_decouple("fpa2bv_mul_x_is_pos", x_is_pos); + dbg_decouple("fpa2bv_mul_x_is_inf", x_is_inf); + dbg_decouple("fpa2bv_mul_y_is_nan", y_is_nan); + dbg_decouple("fpa2bv_mul_y_is_zero", y_is_zero); + dbg_decouple("fpa2bv_mul_y_is_pos", y_is_pos); + dbg_decouple("fpa2bv_mul_y_is_inf", y_is_inf); + + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); + + // (x is NaN) || (y is NaN) -> NaN + m_simp.mk_or(x_is_nan, y_is_nan, c1); + v1 = nan; + + // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. + mk_is_pinf(x, c2); + expr_ref y_sgn_inf(m); + mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); + mk_ite(y_is_zero, nan, y_sgn_inf, v2); + + // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. + mk_is_pinf(y, c3); + expr_ref x_sgn_inf(m); + mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); + mk_ite(x_is_zero, nan, x_sgn_inf, v3); + + // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. + mk_is_ninf(x, c4); + expr_ref neg_y_sgn_inf(m); + mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); + mk_ite(y_is_zero, nan, neg_y_sgn_inf, v4); + + // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. + mk_is_ninf(y, c5); + expr_ref neg_x_sgn_inf(m); + mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); + mk_ite(x_is_zero, nan, neg_x_sgn_inf, v5); + + // (x is 0) || (y is 0) -> x but with sign = x.sign ^ y.sign + m_simp.mk_or(x_is_zero, y_is_zero, c6); + expr_ref sign_xor(m); + m_simp.mk_xor(x_is_pos, y_is_pos, sign_xor); + mk_ite(sign_xor, nzero, pzero, v6); + + // else comes the actual multiplication. + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + SASSERT(ebits <= sbits); + + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); + + dbg_decouple("fpa2bv_mul_a_sig", a_sig); + dbg_decouple("fpa2bv_mul_a_exp", a_exp); + dbg_decouple("fpa2bv_mul_b_sig", b_sig); + dbg_decouple("fpa2bv_mul_b_exp", b_exp); + + expr_ref a_lz_ext(m), b_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + + dbg_decouple("fpa2bv_mul_lz_a", a_lz); + dbg_decouple("fpa2bv_mul_lz_b", b_lz); + + expr_ref a_sig_ext(m), b_sig_ext(m); + a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); + b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); + + expr_ref a_exp_ext(m), b_exp_ext(m); + a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); + b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + expr * signs[2] = { a_sgn, b_sgn }; + res_sgn = m_bv_util.mk_bv_xor(2, signs); + + dbg_decouple("fpa2bv_mul_res_sgn", res_sgn); + + res_exp = m_bv_util.mk_bv_add( + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + + expr_ref product(m); + product = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); + + dbg_decouple("fpa2bv_mul_product", product); + + SASSERT(m_bv_util.get_bv_size(product) == 2*sbits); + + expr_ref h_p(m), l_p(m), rbits(m); + h_p = m_bv_util.mk_extract(2*sbits-1, sbits, product); + l_p = m_bv_util.mk_extract(sbits-1, 0, product); + + if (sbits >= 4) { + expr_ref sticky(m); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits-4, 0, product)); + rbits = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-1, sbits-3, product), sticky); + } + else + rbits = m_bv_util.mk_concat(l_p, m_bv_util.mk_numeral(0, 4 - sbits)); + + SASSERT(m_bv_util.get_bv_size(rbits) == 4); + res_sig = m_bv_util.mk_concat(h_p, rbits); + + //expr_ref rounded(m); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); + + //AZ:the only difference to the original + fix_bits(prec, v7, sbits, ebits); + + // And finally, we tie them together. + mk_ite(c6, v6, v7, result); + mk_ite(c5, v5, result, 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); + + SASSERT(is_well_sorted(m, result)); + + TRACE("fpa2bv_mul", tout << "MUL = " << mk_ismt2_pp(result, m) << std::endl; ); + } + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_div(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_SMALL_FLOATS: + { + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + mk_small_op(f, prec, num, args, small_fd, small_args); + expr_ref small_op(m); + fpa2bv_converter::mk_div(small_fd, num, small_args.c_ptr(), small_op); + mk_cast_small_to_big(f, small_op, result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_div", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + case FPAA_PRECISE: + case FPAA_FIXBITS: // for now, encode fully. + fpa2bv_converter::mk_div(f,num,args,result); + break; + + { + SASSERT(num == 3); + + expr_ref rm(m), x(m), y(m); + rm = args[0]; + x = args[1]; + y = args[2]; + + 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_ninf(f, ninf); + mk_pinf(f, pinf); + + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); + expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); + mk_is_nan(x, x_is_nan); + mk_is_zero(x, x_is_zero); + mk_is_pos(x, x_is_pos); + mk_is_inf(x, x_is_inf); + mk_is_nan(y, y_is_nan); + mk_is_zero(y, y_is_zero); + mk_is_pos(y, y_is_pos); + mk_is_inf(y, y_is_inf); + + dbg_decouple("fpa2bv_div_x_is_nan", x_is_nan); + dbg_decouple("fpa2bv_div_x_is_zero", x_is_zero); + dbg_decouple("fpa2bv_div_x_is_pos", x_is_pos); + dbg_decouple("fpa2bv_div_x_is_inf", x_is_inf); + dbg_decouple("fpa2bv_div_y_is_nan", y_is_nan); + dbg_decouple("fpa2bv_div_y_is_zero", y_is_zero); + dbg_decouple("fpa2bv_div_y_is_pos", y_is_pos); + dbg_decouple("fpa2bv_div_y_is_inf", y_is_inf); + + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); + + // (x is NaN) || (y is NaN) -> NaN + m_simp.mk_or(x_is_nan, y_is_nan, c1); + v1 = nan; + + // (x is +oo) -> if (y is oo) then NaN else inf with y's sign. + mk_is_pinf(x, c2); + expr_ref y_sgn_inf(m); + mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); + mk_ite(y_is_inf, nan, y_sgn_inf, v2); + + // (y is +oo) -> if (x is oo) then NaN else 0 with sign x.sgn ^ y.sgn + mk_is_pinf(y, c3); + expr_ref xy_zero(m), signs_xor(m); + m_simp.mk_xor(x_is_pos, y_is_pos, signs_xor); + mk_ite(signs_xor, nzero, pzero, xy_zero); + mk_ite(x_is_inf, nan, xy_zero, v3); + + // (x is -oo) -> if (y is oo) then NaN else inf with -y's sign. + mk_is_ninf(x, c4); + expr_ref neg_y_sgn_inf(m); + mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); + mk_ite(y_is_inf, nan, neg_y_sgn_inf, v4); + + // (y is -oo) -> if (x is oo) then NaN else 0 with sign x.sgn ^ y.sgn + 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. + c6 = y_is_zero; + expr_ref sgn_inf(m); + mk_ite(signs_xor, ninf, pinf, sgn_inf); + mk_ite(x_is_zero, nan, sgn_inf, v6); + + // (x is 0) -> result is zero with sgn = x.sgn^y.sgn + // This is a special case to avoid problems with the unpacking of zero. + c7 = x_is_zero; + mk_ite(signs_xor, nzero, pzero, v7); + + // else comes the actual division. + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + SASSERT(ebits <= sbits); + + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); + + unsigned extra_bits = sbits+2; + expr_ref a_sig_ext(m), b_sig_ext(m); + a_sig_ext = m_bv_util.mk_concat(a_sig, m_bv_util.mk_numeral(0, sbits + extra_bits)); + b_sig_ext = m_bv_util.mk_zero_extend(sbits + extra_bits, b_sig); + + expr_ref a_exp_ext(m), b_exp_ext(m); + a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); + b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + expr * signs[2] = { a_sgn, b_sgn }; + res_sgn = m_bv_util.mk_bv_xor(2, signs); + + expr_ref a_lz_ext(m), b_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + + res_exp = m_bv_util.mk_bv_sub( + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + + expr_ref quotient(m); + quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV, a_sig_ext, b_sig_ext); + + dbg_decouple("fpa2bv_div_quotient", quotient); + + SASSERT(m_bv_util.get_bv_size(quotient) == (sbits + sbits + extra_bits)); + + expr_ref sticky(m); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); + res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); + + SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); + + //expr_ref rounded(m);//AZ + round(f->get_range(), rm, res_sgn, res_sig, res_exp, v8); + + fix_bits(prec, v8,sbits,ebits);//AZ + + // And finally, we tie them together. + mk_ite(c7, v7, v8, result); + mk_ite(c6, v6, result, result); + mk_ite(c5, v5, result, 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); + + SASSERT(is_well_sorted(m, result)); + + TRACE("fpa2bv_div", tout << "DIV = " << mk_ismt2_pp(result, m) << std::endl; ); + } + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_remainder(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_SMALL_FLOATS: + { + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + mk_small_op(f, prec, num, args, small_fd, small_args); + expr_ref small_op(m); + fpa2bv_converter::mk_rem(small_fd, num, small_args.c_ptr(), small_op); + mk_cast_small_to_big(f, small_op, result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_remainder", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + case FPAA_PRECISE: + fpa2bv_converter::mk_rem(f,num,args,result); + break; + case FPAA_FIXBITS: // for now, encode fully. + { + SASSERT(num == 2); + + // Remainder is always exact, so there is no rounding mode. + expr_ref x(m), y(m); + x = args[0]; + y = args[1]; + + 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_ninf(f, ninf); + mk_pinf(f, pinf); + + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); + expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); + mk_is_nan(x, x_is_nan); + mk_is_zero(x, x_is_zero); + mk_is_pos(x, x_is_pos); + mk_is_inf(x, x_is_inf); + mk_is_nan(y, y_is_nan); + mk_is_zero(y, y_is_zero); + mk_is_pos(y, y_is_pos); + mk_is_inf(y, y_is_inf); + + dbg_decouple("fpa2bv_rem_x_is_nan", x_is_nan); + dbg_decouple("fpa2bv_rem_x_is_zero", x_is_zero); + dbg_decouple("fpa2bv_rem_x_is_pos", x_is_pos); + dbg_decouple("fpa2bv_rem_x_is_inf", x_is_inf); + dbg_decouple("fpa2bv_rem_y_is_nan", y_is_nan); + dbg_decouple("fpa2bv_rem_y_is_zero", y_is_zero); + dbg_decouple("fpa2bv_rem_y_is_pos", y_is_pos); + dbg_decouple("fpa2bv_rem_y_is_inf", y_is_inf); + + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); + + // (x is NaN) || (y is NaN) -> NaN + m_simp.mk_or(x_is_nan, y_is_nan, c1); + v1 = nan; + + // (x is +-oo) -> NaN + c2 = x_is_inf; + v2 = nan; + + // (y is +-oo) -> x + 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; + + // else the actual remainder. + 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); + expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); + + BVSLT(a_exp, b_exp, c6); + v6 = x; + + // max. exponent difference is (2^ebits) - 3 + const mpz & two_to_ebits = fu().fm().m_powers2(ebits); + mpz max_exp_diff; + m_mpz_manager.sub(two_to_ebits, 3, max_exp_diff); + SASSERT(m_mpz_manager.is_int64(max_exp_diff)); + SASSERT(m_mpz_manager.get_uint64(max_exp_diff) <= UINT_MAX); + + unsigned int max_exp_diff_ui = (unsigned int)m_mpz_manager.get_uint64(max_exp_diff); + m_mpz_manager.del(max_exp_diff); + + expr_ref exp_diff(m); + exp_diff = m_bv_util.mk_bv_sub(a_exp, b_exp); + dbg_decouple("fpa2bv_rem_exp_diff", exp_diff); + + // CMW: This creates _huge_ bit-vectors, which is potentially sub-optimal, + // but calculating this via rem = x - y * nearest(x/y) creates huge circuits. + expr_ref huge_sig(m), shifted_sig(m), huge_rem(m); + huge_sig = m_bv_util.mk_zero_extend(max_exp_diff_ui, a_sig); + shifted_sig = m_bv_util.mk_bv_shl(huge_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui + sbits - ebits, exp_diff)); + huge_rem = m_bv_util.mk_bv_urem(shifted_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui, b_sig)); + dbg_decouple("fpa2bv_rem_huge_rem", huge_rem); + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + res_sgn = a_sgn; + res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits, 0, huge_rem), + m_bv_util.mk_numeral(0, 3)); + res_exp = m_bv_util.mk_sign_extend(2, b_exp); + + expr_ref rm(m); + rm = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); + + //expr_ref rounded(m); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); + + fix_bits(prec, v7,sbits, ebits); + + // And finally, we tie them together. + mk_ite(c6, v6, v7, result); + mk_ite(c5, v5, result, 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); + + SASSERT(is_well_sorted(m, result)); + + TRACE("fpa2bv_rem", tout << "REM = " << mk_ismt2_pp(result, m) << std::endl; ); + } + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_abs(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + //This doesn't need approximation in it's current form + switch (m_mode) { + case FPAA_PRECISE: + case FPAA_SMALL_FLOATS: + case FPAA_FIXBITS: // for now, encode fully. + fpa2bv_converter::mk_abs(f,num,args,result); + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_min(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_PRECISE: + case FPAA_SMALL_FLOATS: + case FPAA_FIXBITS: // for now, encode fully. + fpa2bv_converter::mk_min(f,num,args,result); + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_max(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_PRECISE: + case FPAA_SMALL_FLOATS: + case FPAA_FIXBITS: // for now, encode fully. + fpa2bv_converter::mk_max(f,num,args,result); + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_fusedma(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_SMALL_FLOATS: + { + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + mk_small_op(f, prec, num, args, small_fd, small_args); + expr_ref small_op(m); + fpa2bv_converter::mk_fma(small_fd, num, small_args.c_ptr(), small_op); + mk_cast_small_to_big(f, small_op, result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_fma", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + case FPAA_PRECISE: + fpa2bv_converter::mk_fma(f,num,args,result); + break; + case FPAA_FIXBITS: + { + SASSERT(num == 4); + + // fusedma means (x * y) + z + expr_ref rm(m), x(m), y(m), z(m); + rm = args[0]; + x = args[1]; + y = args[2]; + z = args[3]; + + 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_ninf(f, ninf); + mk_pinf(f, pinf); + + expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); + expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); + expr_ref z_is_nan(m), z_is_zero(m), z_is_pos(m), z_is_neg(m), z_is_inf(m); + mk_is_nan(x, x_is_nan); + mk_is_zero(x, x_is_zero); + mk_is_pos(x, x_is_pos); + mk_is_neg(x, x_is_neg); + mk_is_inf(x, x_is_inf); + mk_is_nan(y, y_is_nan); + mk_is_zero(y, y_is_zero); + mk_is_pos(y, y_is_pos); + mk_is_neg(y, y_is_neg); + mk_is_inf(y, y_is_inf); + mk_is_nan(z, z_is_nan); + mk_is_zero(z, z_is_zero); + mk_is_pos(z, z_is_pos); + mk_is_neg(z, z_is_neg); + mk_is_inf(z, z_is_inf); + + expr_ref rm_is_to_neg(m); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + + dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); + dbg_decouple("fpa2bv_fma_x_is_zero", x_is_zero); + dbg_decouple("fpa2bv_fma_x_is_pos", x_is_pos); + dbg_decouple("fpa2bv_fma_x_is_inf", x_is_inf); + dbg_decouple("fpa2bv_fma_y_is_nan", y_is_nan); + dbg_decouple("fpa2bv_fma_y_is_zero", y_is_zero); + dbg_decouple("fpa2bv_fma_y_is_pos", y_is_pos); + dbg_decouple("fpa2bv_fma_y_is_inf", y_is_inf); + dbg_decouple("fpa2bv_fma_z_is_nan", z_is_nan); + dbg_decouple("fpa2bv_fma_z_is_zero", z_is_zero); + dbg_decouple("fpa2bv_fma_z_is_pos", z_is_pos); + dbg_decouple("fpa2bv_fma_z_is_inf", z_is_inf); + + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); + + expr_ref inf_xor(m), inf_cond(m); + m_simp.mk_xor(x_is_neg, y_is_neg, inf_xor); + m_simp.mk_xor(inf_xor, z_is_neg, inf_xor); + m_simp.mk_and(z_is_inf, inf_xor, inf_cond); + + // (x is NaN) || (y is NaN) || (z is Nan) -> NaN + m_simp.mk_or(x_is_nan, y_is_nan, z_is_nan, c1); + v1 = nan; + + // (x is +oo) -> if (y is 0) then NaN else inf with y's sign. + mk_is_pinf(x, c2); + expr_ref y_sgn_inf(m), inf_or(m); + mk_ite(y_is_pos, pinf, ninf, y_sgn_inf); + m_simp.mk_or(y_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, y_sgn_inf, v2); + + // (y is +oo) -> if (x is 0) then NaN else inf with x's sign. + mk_is_pinf(y, c3); + expr_ref x_sgn_inf(m); + mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); + m_simp.mk_or(x_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, x_sgn_inf, v3); + + // (x is -oo) -> if (y is 0) then NaN else inf with -y's sign. + mk_is_ninf(x, c4); + expr_ref neg_y_sgn_inf(m); + mk_ite(y_is_pos, ninf, pinf, neg_y_sgn_inf); + m_simp.mk_or(y_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, neg_y_sgn_inf, v4); + + // (y is -oo) -> if (x is 0) then NaN else inf with -x's sign. + mk_is_ninf(y, c5); + expr_ref neg_x_sgn_inf(m); + mk_ite(x_is_pos, ninf, pinf, neg_x_sgn_inf); + m_simp.mk_or(x_is_zero, inf_cond, inf_or); + mk_ite(inf_or, nan, neg_x_sgn_inf, v5); + + // z is +-INF -> z. + mk_is_inf(z, c6); + v6 = z; + + // (x is 0) || (y is 0) -> z + m_simp.mk_or(x_is_zero, y_is_zero, c7); + expr_ref ite_c(m); + m_simp.mk_and(z_is_zero, m.mk_not(rm_is_to_neg), ite_c); + mk_ite(ite_c, pzero, z, v7); + + + // else comes the fused multiplication. + unsigned ebits = m_util.get_ebits(f->get_range()); + unsigned sbits = m_util.get_sbits(f->get_range()); + SASSERT(ebits <= sbits); + + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + expr_ref c_sgn(m), c_sig(m), c_exp(m), c_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); + unpack(z, c_sgn, c_sig, c_exp, c_lz, true); + + expr_ref a_lz_ext(m), b_lz_ext(m), c_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + c_lz_ext = m_bv_util.mk_zero_extend(2, c_lz); + + expr_ref a_sig_ext(m), b_sig_ext(m); + a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); + b_sig_ext = m_bv_util.mk_zero_extend(sbits, b_sig); + + expr_ref a_exp_ext(m), b_exp_ext(m), c_exp_ext(m); + a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); + b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); + c_exp_ext = m_bv_util.mk_sign_extend(2, c_exp); + + dbg_decouple("fpa2bv_fma_a_sig", a_sig_ext); + dbg_decouple("fpa2bv_fma_b_sig", b_sig_ext); + dbg_decouple("fpa2bv_fma_c_sig", c_sig); + dbg_decouple("fpa2bv_fma_a_exp", a_exp_ext); + dbg_decouple("fpa2bv_fma_b_exp", b_exp_ext); + dbg_decouple("fpa2bv_fma_c_exp", c_exp_ext); + dbg_decouple("fpa2bv_fma_a_lz", a_lz_ext); + dbg_decouple("fpa2bv_fma_b_lz", b_lz_ext); + dbg_decouple("fpa2bv_fma_c_lz", c_lz_ext); + + expr_ref mul_sgn(m), mul_sig(m), mul_exp(m); + expr * signs[2] = { a_sgn, b_sgn }; + + mul_sgn = m_bv_util.mk_bv_xor(2, signs); + dbg_decouple("fpa2bv_fma_mul_sgn", mul_sgn); + + mul_exp = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + dbg_decouple("fpa2bv_fma_mul_exp", mul_exp); + + mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); + dbg_decouple("fpa2bv_fma_mul_sig", mul_sig); + + SASSERT(m_bv_util.get_bv_size(mul_sig) == 2*sbits); + SASSERT(m_bv_util.get_bv_size(mul_exp) == ebits + 2); + + // The product has the form [-1][0].[2*sbits - 2]. + + // Extend c + c_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(c_sig, m_bv_util.mk_numeral(0, sbits-1))); + c_exp_ext = m_bv_util.mk_bv_sub(c_exp_ext, c_lz_ext); + + SASSERT(m_bv_util.get_bv_size(mul_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(c_sig) == 2 * sbits); + + expr_ref swap_cond(m); + swap_cond = m_bv_util.mk_sle(mul_exp, c_exp_ext); + SASSERT(is_well_sorted(m, swap_cond)); + dbg_decouple("fpa2bv_fma_swap_cond", swap_cond); + + expr_ref e_sgn(m), e_sig(m), e_exp(m), f_sgn(m), f_sig(m), f_exp(m); + m_simp.mk_ite(swap_cond, c_sgn, mul_sgn, e_sgn); + m_simp.mk_ite(swap_cond, c_sig, mul_sig, e_sig); // has 2 * sbits + m_simp.mk_ite(swap_cond, c_exp_ext, mul_exp, e_exp); // has ebits + 2 + m_simp.mk_ite(swap_cond, mul_sgn, c_sgn, f_sgn); + m_simp.mk_ite(swap_cond, mul_sig, c_sig, f_sig); // has 2 * sbits + m_simp.mk_ite(swap_cond, mul_exp, c_exp_ext, f_exp); // has ebits + 2 + + SASSERT(is_well_sorted(m, e_sgn)); + SASSERT(is_well_sorted(m, e_sig)); + SASSERT(is_well_sorted(m, e_exp)); + SASSERT(is_well_sorted(m, f_sgn)); + SASSERT(is_well_sorted(m, f_sig)); + SASSERT(is_well_sorted(m, f_exp)); + + SASSERT(m_bv_util.get_bv_size(e_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(f_sig) == 2 * sbits); + SASSERT(m_bv_util.get_bv_size(e_exp) == ebits + 2); + SASSERT(m_bv_util.get_bv_size(f_exp) == ebits + 2); + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + + expr_ref exp_delta(m); + exp_delta = m_bv_util.mk_bv_sub(e_exp, f_exp); + dbg_decouple("fpa2bv_fma_add_exp_delta", exp_delta); + + // cap the delta + expr_ref cap(m), cap_le_delta(m); + cap = m_bv_util.mk_numeral(2*sbits, ebits+2); + cap_le_delta = m_bv_util.mk_ule(cap, exp_delta); + m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); + SASSERT(m_bv_util.get_bv_size(exp_delta) == ebits+2); + SASSERT(is_well_sorted(m, exp_delta)); + dbg_decouple("fpa2bv_fma_add_exp_delta_capped", exp_delta); + + // Alignment shift with sticky bit computation. + expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); + shifted_big = m_bv_util.mk_bv_lshr( + m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), + m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); + shifted_f_sig = m_bv_util.mk_extract(3*sbits-1, sbits, shifted_big); + sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); + SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); + SASSERT(is_well_sorted(m, shifted_f_sig)); + + expr_ref sticky(m); + sticky = m_bv_util.mk_zero_extend(2*sbits-1, m.mk_app(m_bv_util.get_fid(), OP_BREDOR, sticky_raw.get())); + SASSERT(is_well_sorted(m, sticky)); + dbg_decouple("fpa2bv_fma_f_sig_sticky_raw", sticky_raw); + dbg_decouple("fpa2bv_fma_f_sig_sticky", sticky); + + expr * or_args[2] = { shifted_f_sig, sticky }; + shifted_f_sig = m_bv_util.mk_bv_or(2, or_args); + SASSERT(is_well_sorted(m, shifted_f_sig)); + + // Significant addition. + // Two extra bits for catching the overflow. + e_sig = m_bv_util.mk_zero_extend(2, e_sig); + shifted_f_sig = m_bv_util.mk_zero_extend(2, shifted_f_sig); + + expr_ref eq_sgn(m); + m_simp.mk_eq(e_sgn, f_sgn, eq_sgn); + + SASSERT(m_bv_util.get_bv_size(e_sig) == 2*sbits + 2); + SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2*sbits + 2); + + dbg_decouple("fpa2bv_fma_add_e_sig", e_sig); + dbg_decouple("fpa2bv_fma_add_shifted_f_sig", shifted_f_sig); + + expr_ref sum(m); + m_simp.mk_ite(eq_sgn, + m_bv_util.mk_bv_add(e_sig, shifted_f_sig), + m_bv_util.mk_bv_sub(e_sig, shifted_f_sig), + sum); + + SASSERT(is_well_sorted(m, sum)); + + dbg_decouple("fpa2bv_fma_add_sum", sum); + + expr_ref sign_bv(m), n_sum(m); + sign_bv = m_bv_util.mk_extract(2*sbits+1, 2*sbits+1, sum); + n_sum = m_bv_util.mk_bv_neg(sum); + + expr_ref res_sig_eq(m), sig_abs(m), one_1(m); + one_1 = m_bv_util.mk_numeral(1, 1); + m_simp.mk_eq(sign_bv, one_1, res_sig_eq); + m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); + dbg_decouple("fpa2bv_fma_add_sign_bv", sign_bv); + dbg_decouple("fpa2bv_fma_add_n_sum", n_sum); + dbg_decouple("fpa2bv_fma_add_sig_abs", sig_abs); + + res_exp = e_exp; + + // Result could overflow into 4.xxx ... + + family_id bvfid = m_bv_util.get_fid(); + expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); + expr_ref not_e_sgn(m), not_f_sgn(m), not_sign_bv(m); + not_e_sgn = m_bv_util.mk_bv_not(e_sgn); + not_f_sgn = m_bv_util.mk_bv_not(f_sgn); + not_sign_bv = m_bv_util.mk_bv_not(sign_bv); + res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, e_sgn, sign_bv); + res_sgn_c2 = m.mk_app(bvfid, OP_BAND, e_sgn, not_f_sgn, not_sign_bv); + res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); + 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())); + dbg_decouple("fpa2bv_fma_add_sum_sticky", sticky); + + 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); + SASSERT(is_well_sorted(m, is_zero_sig)); + + dbg_decouple("fpa2bv_fma_is_zero_sig", is_zero_sig); + + expr_ref zero_case(m); + mk_ite(rm_is_to_neg, nzero, pzero, zero_case); + + + //AZ: Should the zero case be constrained in some manner? + expr_ref rounded(m);//, fixed(m); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); + + fix_bits(prec,rounded,sbits, ebits); + + mk_ite(is_zero_sig, zero_case, rounded, v8); + + // And finally, we tie them together. + mk_ite(c7, v7, v8, result); + mk_ite(c6, v6, result, result); + mk_ite(c5, v5, result, 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); + + SASSERT(is_well_sorted(m, result)); + + TRACE("fpa2bv_fma_", tout << "FMA = " << mk_ismt2_pp(result, m) << std::endl; ); + } + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_sqrt(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_SMALL_FLOATS: + { + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + mk_small_op(f, prec, num, args, small_fd, small_args); + expr_ref small_op(m); + fpa2bv_converter::mk_sqrt(small_fd, num, small_args.c_ptr(), small_op); + mk_cast_small_to_big(f, small_op, result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_sqrt", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + case FPAA_PRECISE: + case FPAA_FIXBITS: // for now, encode fully. + fpa2bv_converter::mk_sqrt(f,num,args,result); + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::mk_round_to_integral(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) { + switch (m_mode) { + case FPAA_SMALL_FLOATS: + { + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + mk_small_op(f, prec, num, args, small_fd, small_args); + expr_ref small_op(m); + fpa2bv_converter::mk_round_to_integral(small_fd, num, small_args.c_ptr(), small_op); + mk_cast_small_to_big(f, small_op, result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_r2i", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + case FPAA_PRECISE: + case FPAA_FIXBITS: // for now, encode fully. + fpa2bv_converter::mk_sqrt(f,num,args,result); + break; + default: UNREACHABLE(); break; + } +} + +void fpa2bv_converter_prec::establish_sort(unsigned num, expr * const * args, unsigned & ebits, unsigned & sbits) +{ + unsigned ne,ns; + //hardcoding for now + ebits = 3; + sbits = 3; + for(unsigned i=0;iget_decl()->get_range())) + { + ne = fu().get_ebits(s); + ns = fu().get_sbits(s); + ebits = (ne < ebits)?ebits:ne; + sbits = (ns < sbits)?sbits:ns; + } + } +} + +void fpa2bv_converter_prec::mk_float_eq(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +{ + switch (m_mode) { + case FPAA_PRECISE: + case FPAA_FIXBITS: + fpa2bv_converter::mk_float_eq(f,num,args,result); + break; + case FPAA_SMALL_FLOATS: + { + unsigned sbits, ebits; + + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + establish_sort(num, args, ebits, sbits); + mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); + fpa2bv_converter::mk_float_eq(small_fd, num, small_args.c_ptr(), result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + } + +} + +void fpa2bv_converter_prec::mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +{ + switch (m_mode){ + case FPAA_PRECISE: + case FPAA_FIXBITS: + fpa2bv_converter::mk_float_lt(f,num,args,result); + break; + case FPAA_SMALL_FLOATS: { + unsigned sbits, ebits; + + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + establish_sort(num, args, ebits, sbits); + mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); + fpa2bv_converter::mk_float_lt(small_fd, num, small_args.c_ptr(), result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + } +} +void fpa2bv_converter_prec::mk_float_gt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +{ + switch (m_mode){ + case FPAA_PRECISE: + case FPAA_FIXBITS: + fpa2bv_converter::mk_float_gt(f,num,args,result); + break; + case FPAA_SMALL_FLOATS: { + unsigned sbits, ebits; + + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + establish_sort(num, args, ebits, sbits); + mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); + fpa2bv_converter::mk_float_gt(small_fd, num, small_args.c_ptr(), result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + } +} +void fpa2bv_converter_prec::mk_float_le(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +{ + switch (m_mode){ + case FPAA_PRECISE: + case FPAA_FIXBITS: + fpa2bv_converter::mk_float_le(f,num,args,result); + break; + case FPAA_SMALL_FLOATS: { + unsigned sbits, ebits; + + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + establish_sort(num, args, ebits, sbits); + mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); + fpa2bv_converter::mk_float_le(small_fd, num, small_args.c_ptr(), result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + } +} +void fpa2bv_converter_prec::mk_float_ge(func_decl * f, unsigned num, expr * const * args, expr_ref & result) +{ + switch (m_mode){ + case FPAA_PRECISE: + case FPAA_FIXBITS: + fpa2bv_converter::mk_float_ge(f,num,args,result); + break; + case FPAA_SMALL_FLOATS: { + unsigned sbits, ebits; + + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + establish_sort(num, args, ebits, sbits); + mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); + fpa2bv_converter::mk_float_ge(small_fd, num, small_args.c_ptr(), result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + } +} +/* + +void fpa2bv_converter_prec::mk_is_zero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result) +{ + switch (m_mode){ + case FPAA_PRECISE: + case FPAA_FIXBITS: + fpa2bv_converter::mk_float_eq(f,num,args,result); + break; + case FPAA_SMALL_FLOATS: { + unsigned sbits, ebits; + + func_decl_ref small_fd(m); + expr_ref_vector small_args(m); + establish_sort(num, args, ebits, sbits); + mk_small_op(f, ebits, sbits, num, args, small_fd, small_args); + fpa2bv_converter::mk_is_zero(small_fd, num, small_args.c_ptr(), result); + SASSERT(is_well_sorted(m, result)); + TRACE("fpa2bv_small_float_add", tout << mk_ismt2_pp(result, m) << std::endl; ); + break; + } + } +} +void fpa2bv_converter_prec::mk_is_nzero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); +void fpa2bv_converter_prec::mk_is_pzero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); +void fpa2bv_converter_prec::mk_is_sign_minus(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); +void fpa2bv_converter_prec::mk_is_nan(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); +void fpa2bv_converter_prec::mk_is_inf(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); +void fpa2bv_converter_prec::mk_is_normal(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); +void fpa2bv_converter_prec::mk_is_subnormal(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); +*/ + + + + + + diff --git a/src/tactic/fpa/fpa2bv_converter_prec.h b/src/tactic/fpa/fpa2bv_converter_prec.h new file mode 100644 index 000000000..24631b1ff --- /dev/null +++ b/src/tactic/fpa/fpa2bv_converter_prec.h @@ -0,0 +1,103 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + fpa2bv_converter_prec.h + +Abstract: + + Conversion routines for Floating Point -> Bit-Vector + +Author: + + Christoph (cwinter) 2012-02-09 + +Notes: + +--*/ +#ifndef _FPA2BV_CONVERTER_PREC +#define _FPA2BV_CONVERTER_PREC + +#include"ast.h" +#include"obj_hashtable.h" +#include"ref_util.h" +#include"fpa_decl_plugin.h" +#include"bv_decl_plugin.h" +#include"model_converter.h" +#include"basic_simplifier_plugin.h" +#include"fpa2bv_converter.h" + +#define MAX_PRECISION 100 +#define MIN_PRECISION 0 + +class fpa2bv_prec_model_converter; + +enum fpa_approximation_mode { + FPAA_PRECISE, // Always use precise encoding + FPAA_FIXBITS, // Approximate by fixing some bits of the encoding + FPAA_SMALL_FLOATS // Approximate by using smaller floats +}; + +#define FPAA_DEFAULT_MODE FPAA_SMALL_FLOATS + +class fpa2bv_converter_prec : public fpa2bv_converter { + fpa_approximation_mode m_mode; + + void fix_bits(unsigned prec, expr_ref rounded, unsigned sbits, unsigned ebits);//expr_ref& fixed, + + void mk_small_op(func_decl * f, unsigned prec, unsigned num, expr * const * args, func_decl_ref & small_op, expr_ref_vector & cast_args); + void mk_small_op(func_decl * f, unsigned new_ebits, unsigned new_sbits, unsigned num, expr * const * args, func_decl_ref & small_op, expr_ref_vector & cast_args); + void mk_cast_small_to_big(func_decl * big_fd, expr * arg, expr_ref & result); + void mk_cast_small_to_big(unsigned sbits, unsigned ebits, expr * arg, expr_ref & result); + void match_sorts(expr * a, expr * b, expr_ref & n_a, expr_ref & n_b); + void establish_sort(unsigned num, expr * const * args, unsigned & ebits, unsigned & sbits); +public: + fpa2bv_converter_prec(ast_manager & m, fpa_approximation_mode mode); + + void mk_const(func_decl * f, unsigned prec, expr_ref & result); + + void mk_eq(expr * a, expr * b, expr_ref & result); + void mk_ite(expr * c, expr * t, expr * f, expr_ref & result); + + void mk_add(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_sub(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_uminus(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_mul(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_div(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_remainder(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_abs(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_min(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_max(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_fusedma(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_sqrt(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_round_to_integral(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + + void mk_float_eq(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_float_gt(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_float_le(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + void mk_float_ge(func_decl * f, unsigned num, expr * const * args, expr_ref & result); + + /* + void mk_is_zero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_is_nzero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_is_pzero(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_is_sign_minus(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_is_nan(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_is_inf(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_is_normal(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + void mk_is_subnormal(func_decl * f, unsigned prec, unsigned num, expr * const * args, expr_ref & result); + */ + + + + void reset() { + dec_ref_map_key_values(m, m_const2bv); + dec_ref_map_key_values(m, m_rm_const2bv); + } +}; + + + +#endif diff --git a/src/tactic/fpa/fpa2bv_rewriter_prec.h b/src/tactic/fpa/fpa2bv_rewriter_prec.h new file mode 100644 index 000000000..051790ac3 --- /dev/null +++ b/src/tactic/fpa/fpa2bv_rewriter_prec.h @@ -0,0 +1,261 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + fpa2bv_rewriter.h + +Abstract: + + Rewriter for converting FPA to BV + +Author: + + Christoph (cwinter) 2012-02-09 + +Notes: + +--*/ + +#ifndef _FPA2BV_REWRITER_H_ +#define _FPA2BV_REWRITER_H_ + +#include"cooperate.h" +#include"rewriter_def.h" +#include"bv_decl_plugin.h" +#include"fpa2bv_converter_prec.h" +#include"tactic_exception.h" +#include + +struct fpa2bv_rewriter_prec_cfg : public default_rewriter_cfg { + ast_manager & m_manager; + expr_ref_vector m_out; + fpa2bv_converter_prec & m_conv; + obj_map * cnst2prec_map; + + unsigned precision; + unsigned long long m_max_memory; + unsigned m_max_steps; + + ast_manager & m() const { return m_manager; } + + fpa2bv_rewriter_prec_cfg(ast_manager & m, fpa2bv_converter_prec & c, params_ref const & p): + m_manager(m), + m_out(m), + m_conv(c) { + updt_params(p); + // We need to make sure that the mananger has the BV plugin loaded. + symbol s_bv("bv"); + if (!m_manager.has_plugin(s_bv)) + m_manager.register_plugin(s_bv, alloc(bv_decl_plugin)); + } + + ~fpa2bv_rewriter_prec_cfg() { + } + + void cleanup_buffers() { + m_out.finalize(); + } + + unsigned get_precision(func_decl * f){ + if(cnst2prec_map->contains(f)) + return cnst2prec_map->find(f); + else return precision; + } + void set_precision(unsigned p) { precision=p; } + void set_mappings(obj_map * o2p) + { + this->cnst2prec_map=o2p; + } + + void updt_params(params_ref const & p) { + m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_max_steps = p.get_uint("max_steps", UINT_MAX); + } + + bool max_steps_exceeded(unsigned num_steps) const { + cooperate("fpa2bv"); + return num_steps > m_max_steps; + } + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + TRACE("fpa2bv_rw", tout << "APP: " << f->get_name() << std::endl; ); + + if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_float(f->get_range())) { + m_conv.mk_const(f, get_precision(f), result); + return BR_DONE; + } + + if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm(f->get_range())) { + m_conv.mk_rm_const(f, result); + return BR_DONE; + } + + if (m().is_eq(f)) { + SASSERT(num == 2); + //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); + return BR_DONE; + } + else if (m_conv.is_rm(ds)) { + result = m().mk_eq(args[0], args[1]); + return BR_DONE; + } + return BR_FAILED; + } + + 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); + return BR_DONE; + } + return BR_FAILED; + } + + expr_ref newAssertion(m_manager); + + if (m_conv.is_float_family(f)) + { + switch (f->get_decl_kind()) + { + case OP_FPA_RM_NEAREST_TIES_TO_AWAY: + case OP_FPA_RM_NEAREST_TIES_TO_EVEN: + case OP_FPA_RM_TOWARD_NEGATIVE: + case OP_FPA_RM_TOWARD_POSITIVE: + case OP_FPA_RM_TOWARD_ZERO: m_conv.mk_rounding_mode(f, result); return BR_DONE; + case OP_FPA_NUM: m_conv.mk_numeral(f, num, args, result); return BR_DONE; + case OP_FPA_PLUS_INF: m_conv.mk_pinf(f, result); return BR_DONE; + case OP_FPA_MINUS_INF: m_conv.mk_ninf(f, result); return BR_DONE; + case OP_FPA_PLUS_ZERO: m_conv.mk_pzero(f, result); return BR_DONE; + case OP_FPA_MINUS_ZERO: m_conv.mk_nzero(f, result); return BR_DONE; + case OP_FPA_NAN: m_conv.mk_nan(f, result); return BR_DONE; + case OP_FPA_ADD: + m_conv.mk_add(f,get_precision(f), num, args, result);return BR_DONE; + case OP_FPA_SUB: + m_conv.mk_sub(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_NEG: + m_conv.mk_uminus(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_MUL: + m_conv.mk_mul(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_DIV: + m_conv.mk_div(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_REM: + m_conv.mk_remainder(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_ABS: m_conv.mk_abs(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_MIN: m_conv.mk_min(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_MAX: m_conv.mk_max(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_FMA: + m_conv.mk_fusedma(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_SQRT: + m_conv.mk_sqrt(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_ROUND_TO_INTEGRAL: m_conv.mk_round_to_integral(f, get_precision(f), num, args, result); return BR_DONE; + case OP_FPA_EQ: m_conv.mk_float_eq(f, num, args, result); return BR_DONE; + case OP_FPA_LT: m_conv.mk_float_lt(f, num, args, result); return BR_DONE; + case OP_FPA_GT: m_conv.mk_float_gt(f, num, args, result); return BR_DONE; + case OP_FPA_LE: m_conv.mk_float_le(f, num, args, result); return BR_DONE; + case OP_FPA_GE: m_conv.mk_float_ge(f, num, args, result); return BR_DONE; + case OP_FPA_IS_ZERO: m_conv.mk_is_zero(f, num, args, result); return BR_DONE; + case OP_FPA_IS_NAN: m_conv.mk_is_nan(f, num, args, result); return BR_DONE; + case OP_FPA_IS_INF: m_conv.mk_is_inf(f, num, args, result); return BR_DONE; + case OP_FPA_IS_NORMAL: m_conv.mk_is_normal(f, num, args, result); return BR_DONE; + case OP_FPA_IS_SUBNORMAL: m_conv.mk_is_subnormal(f, num, args, result); return BR_DONE; + case OP_FPA_IS_POSITIVE: m_conv.mk_is_positive(f, num, args, result); return BR_DONE; + case OP_FPA_IS_NEGATIVE: m_conv.mk_is_negative(f, num, args, result); return BR_DONE; + case OP_FPA_TO_FP: m_conv.mk_to_fp(f, num, args, result); return BR_DONE; + case OP_FPA_TO_FP_UNSIGNED: m_conv.mk_to_fp_unsigned(f, num, args, result); return BR_DONE; + case OP_FPA_FP: m_conv.mk_fp(f, num, args, result); return BR_DONE; + case OP_FPA_TO_UBV: m_conv.mk_to_ubv(f, num, args, result); return BR_DONE; + case OP_FPA_TO_SBV: m_conv.mk_to_sbv(f, num, args, result); return BR_DONE; + case OP_FPA_TO_REAL: m_conv.mk_to_real(f, num, args, result); return BR_DONE; + case OP_FPA_TO_IEEE_BV: m_conv.mk_to_ieee_bv(f, num, args, result); return BR_DONE; + case OP_FPA_INTERNAL_BVWRAP: + case OP_FPA_INTERNAL_BVUNWRAP: + case OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED: + case OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED: + case OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED: return BR_FAILED; + default: + TRACE("fpa2bv", tout << "unsupported operator: " << f->get_name() << "\n"; + for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); + NOT_IMPLEMENTED_YET(); + } + } + + if (f->get_family_id() == null_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++; + } + + if (is_float_uf) + { + m_conv.mk_uninterpreted_function(f, num, args, result); + return BR_DONE; + } + } + + return BR_FAILED; + } + + bool pre_visit(expr * t) + { + TRACE("pre_visit_prec", tout << mk_ismt2_pp(t, m()) << std::endl;); + + if(t->get_kind() == AST_APP && is_app_of(t, to_app(t)->get_family_id(), OP_EQ)) { + //Equation over non-boolean expressions, it should be of form constantI = subexprI + app * a = to_app(t); + + if (a->get_num_args() == 2) { + expr * a0 = a->get_arg(0); + expr * a1 = a->get_arg(1); + func_decl * cnst = 0; + + if (a0->get_kind() == AST_APP && cnst2prec_map->contains(to_app(a0)->get_decl())) + cnst = to_app(a0)->get_decl(); + else if (a1->get_kind() == AST_APP && cnst2prec_map->contains(to_app(a1)->get_decl())) + cnst = to_app(a1)->get_decl(); + + if (cnst == 0) { + // For all equalities that were in the original problem, we don't + // have any precision tracking, so those simply get 100% precision. + set_precision(100); + } + else + set_precision(cnst2prec_map->find(cnst)); + TRACE("pre_visit_prec", tout << "Precision = " << get_precision(NULL) << std::endl;); + } + } + return true; + } + + bool reduce_quantifier(quantifier * old_q, + expr * new_body, + expr * const * new_patterns, + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr) { + return false; + } + + bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { + return false; + } +}; + +template class rewriter_tpl; + +struct fpa2bv_rewriter_prec : public rewriter_tpl { + fpa2bv_rewriter_prec_cfg m_cfg; + fpa2bv_rewriter_prec(ast_manager & m, fpa2bv_converter_prec & c, params_ref const & p): + rewriter_tpl(m, m.proofs_enabled(), m_cfg), + m_cfg(m, c, p) { + } +}; + +#endif diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index 5fb35d972..9149f1b9a 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -88,6 +88,22 @@ class fpa2bv_tactic : public tactic { new_pr = m.mk_modus_ponens(pr, new_pr); } g->update(idx, new_curr, new_pr, g->dep(idx)); + + if (is_app(new_curr)) { + const app * a = to_app(new_curr.get()); + if (a->get_family_id() == m_conv.fu().get_family_id() && + a->get_decl_kind() == OP_FPA_IS_NAN) { + // Inject auxiliary lemmas that fix e to the one and only NaN value, + // that is (= e (fp #b0 #b1...1 #b0...01)), so that the value propagation + // has a value to propagate. + expr * sgn, *sig, *exp; + expr_ref top_exp(m); + m_conv.split_fp(new_curr, sgn, exp, sig); + m.mk_eq(sgn, m_conv.bu().mk_numeral(0, 1)); + m.mk_eq(exp, m_conv.bu().mk_numeral(-1, m_conv.bu().get_bv_size(exp))); + m.mk_eq(sig, m_conv.bu().mk_numeral(1, m_conv.bu().get_bv_size(sig))); + } + } } if (g->models_enabled()) diff --git a/src/tactic/fpa/qffp_tactic.cpp b/src/tactic/fpa/qffp_tactic.cpp index c45898cc7..485e837ee 100644 --- a/src/tactic/fpa/qffp_tactic.cpp +++ b/src/tactic/fpa/qffp_tactic.cpp @@ -23,9 +23,47 @@ Notes: #include"fpa2bv_tactic.h" #include"smt_tactic.h" #include"propagate_values_tactic.h" +#include"probe_arith.h" +#include"qfnra_tactic.h" #include"qffp_tactic.h" + +struct has_fp_to_real_predicate { + struct found {}; + ast_manager & m; + bv_util bu; + fpa_util fu; + arith_util au; + + has_fp_to_real_predicate(ast_manager & _m) : m(_m), bu(m), fu(m), au(m) {} + + void operator()(var *) { throw found(); } + + void operator()(quantifier *) { throw found(); } + + void operator()(app * n) { + sort * s = get_sort(n); + if (au.is_real(s) && n->get_family_id() == fu.get_family_id() && + is_app(n) && to_app(n)->get_decl_kind() == OP_FPA_TO_REAL) + throw found(); + } +}; + +class has_fp_to_real_probe : public probe { +public: + virtual result operator()(goal const & g) { + return test(g); + } + + virtual ~has_fp_to_real_probe() {} +}; + +probe * mk_has_fp_to_real_probe() { + return alloc(has_fp_to_real_probe); +} + + tactic * mk_qffp_tactic(ast_manager & m, params_ref const & p) { params_ref simp_p = p; simp_p.set_bool("arith_lhs", true); @@ -37,12 +75,16 @@ tactic * mk_qffp_tactic(ast_manager & m, params_ref const & p) { mk_smt_tactic(), and_then( mk_fpa2bv_tactic(m, p), + mk_propagate_values_tactic(m, p), using_params(mk_simplify_tactic(m, p), simp_p), mk_bit_blaster_tactic(m, p), - using_params(mk_simplify_tactic(m, p), simp_p), + using_params(mk_simplify_tactic(m, p), simp_p), cond(mk_is_propositional_probe(), mk_sat_tactic(m, p), - mk_smt_tactic(p)), + cond(mk_has_fp_to_real_probe(), + mk_qfnra_tactic(m, p), + mk_smt_tactic(p)) + ), mk_fail_if_undecided_tactic()))); st->updt_params(p); diff --git a/src/tactic/horn_subsume_model_converter.cpp b/src/tactic/horn_subsume_model_converter.cpp index ced4e657b..25da094fa 100644 --- a/src/tactic/horn_subsume_model_converter.cpp +++ b/src/tactic/horn_subsume_model_converter.cpp @@ -43,7 +43,7 @@ bool horn_subsume_model_converter::mk_horn( app* head, expr* body, func_decl_ref& pred, expr_ref& body_res) { expr_ref_vector conjs(m), subst(m); - ptr_vector sorts, sorts2; + ptr_vector sorts2; var_subst vs(m, false); if (!is_uninterp(head)) { @@ -53,28 +53,27 @@ bool horn_subsume_model_converter::mk_horn( pred = head->get_decl(); unsigned arity = head->get_num_args(); - get_free_vars(head, sorts); - get_free_vars(body, sorts); + expr_free_vars fv; + fv(head); + fv.accumulate(body); - if (arity == 0 && sorts.empty()) { + if (arity == 0 && fv.empty()) { body_res = body; return true; } + fv.set_default_sort(m.mk_bool_sort()); svector names; - for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i]) { - sorts[i] = m.mk_bool_sort(); - } + for (unsigned i = 0; i < fv.size(); ++i) { names.push_back(symbol(i)); } names.reverse(); - sorts.reverse(); + fv.reverse(); conjs.push_back(body); for (unsigned i = 0; i < arity; ++i) { expr* arg = head->get_arg(i); var_ref v(m); - v = m.mk_var(sorts.size()+i, m.get_sort(arg)); + v = m.mk_var(fv.size()+i, m.get_sort(arg)); if (is_var(arg)) { unsigned w = to_var(arg)->get_idx(); @@ -101,12 +100,12 @@ bool horn_subsume_model_converter::mk_horn( vs(tmp, subst.size(), subst.c_ptr(), body_expr); } - if (sorts.empty()) { + if (fv.empty()) { SASSERT(subst.empty()); body_res = body_expr; } else { - body_res = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), body_expr.get()); + body_res = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), body_expr.get()); m_rewrite(body_res); } @@ -120,10 +119,9 @@ bool horn_subsume_model_converter::mk_horn( bool horn_subsume_model_converter::mk_horn( expr* clause, func_decl_ref& pred, expr_ref& body) { - ptr_vector sorts; // formula is closed. - DEBUG_CODE(get_free_vars(clause, sorts); SASSERT(sorts.empty());); + DEBUG_CODE(expr_free_vars fv; fv(clause); SASSERT(fv.empty());); while (is_quantifier(clause) && to_quantifier(clause)->is_forall()) { quantifier* q = to_quantifier(clause); diff --git a/src/tactic/horn_subsume_model_converter.h b/src/tactic/horn_subsume_model_converter.h index 993f29cc9..8bad28e3d 100644 --- a/src/tactic/horn_subsume_model_converter.h +++ b/src/tactic/horn_subsume_model_converter.h @@ -72,7 +72,7 @@ public: void insert(func_decl* p, expr* body) { m_funcs.push_back(p); m_bodies.push_back(body); } - virtual void operator()(model_ref& m); + virtual void operator()(model_ref& _m); virtual model_converter * translate(ast_translation & translator); diff --git a/src/tactic/nlsat_smt/nl_purify_tactic.cpp b/src/tactic/nlsat_smt/nl_purify_tactic.cpp new file mode 100644 index 000000000..91c462486 --- /dev/null +++ b/src/tactic/nlsat_smt/nl_purify_tactic.cpp @@ -0,0 +1,722 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + nl_purify_tactic.cpp + +Abstract: + + Tactic for purifying quantifier-free formulas that mix QF_NRA and other theories. + It is designed to allow cooprating between the nlsat solver and other theories + in a decoupled way. + + Let goal be formula F. + Let NL goal be formula G. + Assume F is in NNF. + Assume F does not contain mix of real/integers. + Assume F is quantifier-free (please, otherwise we need to reprocess from instantiated satisfiable formula) + + For each atomic nl formula f, + - introduce a propositional variable p + - replace f by p + - add clauses p => f to G + + For each interface term t, + - introduce interface variable v (or use t if it is already a variable) + - replace t by v + + Check satisfiability of G. + If satisfiable, then check assignment to p and interface equalities on F + If unsat: + Retrieve core and add core to G. + else: + For interface equalities from model of F that are not equal in G, add + For interface variables that are equal under one model, but not the other model, + create interface predicate p_vw => v = w, add to both F, G. + Add interface equations to assumptions, recheck F. + If unsat retrieve core add to G. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-5-5. + +Revision History: + +--*/ +#include "tactical.h" +#include "nl_purify_tactic.h" +#include "smt_tactic.h" +#include "rewriter.h" +#include "nlsat_tactic.h" +#include "filter_model_converter.h" +#include "obj_pair_hashtable.h" +#include "rewriter_def.h" +#include "ast_pp.h" +#include "trace.h" +#include "smt_solver.h" +#include "solver.h" +#include "model_smt2_pp.h" +#include "expr_safe_replace.h" + +class nl_purify_tactic : public tactic { + + enum polarity_t { + pol_pos, + pol_neg, + pol_dual + }; + + ast_manager & m; + arith_util m_util; + params_ref m_params; + bool m_produce_proofs; + ref m_fmc; + bool m_cancel; + tactic_ref m_nl_tac; // nlsat tactic + goal_ref m_nl_g; // nlsat goal + ref m_solver; // SMT solver + expr_ref_vector m_eq_preds; // predicates for equality between pairs of interface variables + svector m_eq_values; // truth value of the equality predicates in nlsat + app_ref_vector m_new_reals; // interface real variables + app_ref_vector m_new_preds; // abstraction predicates for smt_solver (hide real constraints) + expr_ref_vector m_asms; // assumptions to pass to SMT solver + obj_pair_map m_eq_pairs; // map pairs of interface variables to auxiliary predicates + obj_map m_interface_cache; // map of compound real expression to interface variable. + obj_map m_polarities; // polarities of sub-expressions + +public: + struct rw_cfg : public default_rewriter_cfg { + enum mode_t { + mode_interface_var, + mode_bool_preds + }; + ast_manager& m; + nl_purify_tactic & m_owner; + app_ref_vector& m_new_reals; + app_ref_vector& m_new_preds; + obj_map& m_polarities; + obj_map& m_interface_cache; + expr_ref_vector m_args; + mode_t m_mode; + + rw_cfg(nl_purify_tactic & o): + m(o.m), + m_owner(o), + m_new_reals(o.m_new_reals), + m_new_preds(o.m_new_preds), + m_polarities(o.m_polarities), + m_interface_cache(o.m_interface_cache), + m_args(m), + m_mode(mode_interface_var) { + } + + virtual ~rw_cfg() {} + + arith_util & u() { return m_owner.m_util; } + + bool produce_proofs() const { return m_owner.m_produce_proofs; } + + expr * mk_interface_var(expr* arg) { + expr* r; + if (m_interface_cache.find(arg, r)) { + return r; + } + if (is_uninterp_const(arg)) { + m_interface_cache.insert(arg, arg); + return arg; + } + r = m.mk_fresh_const(0, u().mk_real()); + m_new_reals.push_back(to_app(r)); + m_owner.m_fmc->insert(to_app(r)->get_decl()); + m_interface_cache.insert(arg, r); + expr_ref eq(m); + eq = m.mk_eq(r, arg); + if (is_real_expression(arg)) { + m_owner.m_nl_g->assert_expr(eq); // m.mk_oeq(r, arg) + } + else { + m_owner.m_solver->assert_expr(eq); + } + return r; + } + + bool is_real_expression(expr* e) { + return is_app(e) && (to_app(e)->get_family_id() == u().get_family_id()); + } + + void mk_interface_bool(func_decl * f, unsigned num, expr* const* args, expr_ref& result) { + expr_ref old_pred(m.mk_app(f, num, args), m); + polarity_t pol; + VERIFY(m_polarities.find(old_pred, pol)); + result = m.mk_fresh_const(0, m.mk_bool_sort()); + m_polarities.insert(result, pol); + m_new_preds.push_back(to_app(result)); + m_owner.m_fmc->insert(to_app(result)->get_decl()); + if (pol != pol_neg) { + m_owner.m_nl_g->assert_expr(m.mk_or(m.mk_not(result), m.mk_app(f, num, args))); + } + if (pol != pol_pos) { + m_owner.m_nl_g->assert_expr(m.mk_or(result, m.mk_not(m.mk_app(f, num, args)))); + } + TRACE("nlsat_smt", tout << old_pred << " : " << result << "\n";); + } + + bool reduce_quantifier(quantifier * old_q, + expr * new_body, + expr * const * new_patterns, + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr) { + throw tactic_exception("quantifiers are not supported in mixed-mode nlsat engine"); + } + + br_status reduce_app(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { + if (m_mode == mode_bool_preds) { + return reduce_app_bool(f, num, args, result, pr); + } + else { + return reduce_app_real(f, num, args, result, pr); + } + } + + br_status reduce_app_bool(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { + if (f->get_family_id() == m.get_basic_family_id()) { + if (f->get_decl_kind() == OP_EQ && u().is_real(args[0])) { + mk_interface_bool(f, num, args, result); + return BR_DONE; + } + else { + return BR_FAILED; + } + } + if (f->get_family_id() == u().get_family_id()) { + switch (f->get_decl_kind()) { + case OP_LE: case OP_GE: case OP_LT: case OP_GT: + // these are the only real cases of non-linear atomic formulas besides equality. + mk_interface_bool(f, num, args, result); + return BR_DONE; + default: + return BR_FAILED; + } + } + return BR_FAILED; + } + + br_status reduce_app_real(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref & pr) { + bool has_interface = false; + if (f->get_family_id() == u().get_family_id()) { + switch (f->get_decl_kind()) { + case OP_NUM: case OP_IRRATIONAL_ALGEBRAIC_NUM: + case OP_ADD: case OP_MUL: case OP_SUB: + case OP_UMINUS: case OP_ABS: case OP_POWER: + return BR_FAILED; + default: + break; + } + } + m_args.reset(); + for (unsigned i = 0; i < num; ++i) { + expr* arg = args[i]; + if (u().is_real(arg)) { + has_interface = true; + m_args.push_back(mk_interface_var(arg)); + } + else { + m_args.push_back(arg); + } + } + if (has_interface) { + result = m.mk_app(f, num, m_args.c_ptr()); + TRACE("nlsat_smt", tout << result << "\n";); + return BR_DONE; + } + else { + return BR_FAILED; + } + } + }; + +private: + + class rw : public rewriter_tpl { + rw_cfg m_cfg; + public: + rw(nl_purify_tactic & o): + rewriter_tpl(o.m, o.m_produce_proofs, m_cfg), + m_cfg(o) { + } + void set_bool_mode() { + m_cfg.m_mode = rw_cfg::mode_bool_preds; + } + void set_interface_var_mode() { + m_cfg.m_mode = rw_cfg::mode_interface_var; + } + }; + + arith_util & u() { return m_util; } + + void check_point() { + if (m_cancel) { + throw tactic_exception("canceled"); + } + } + + void display_result(std::ostream& out, goal_ref_buffer const& result) { + for (unsigned i = 0; i < result.size(); ++i) { + result[i]->display(out << "goal\n"); + } + } + + void update_eq_values(model_ref& mdl) { + expr_ref tmp(m); + for (unsigned i = 0; i < m_eq_preds.size(); ++i) { + expr* pred = m_eq_preds[i].get(); + m_eq_values[i] = l_undef; + if (mdl->eval(pred, tmp)) { + if (m.is_true(tmp)) { + m_eq_values[i] = l_true; + } + else if (m.is_false(tmp)) { + m_eq_values[i] = l_false; + } + } + } + } + + void solve(goal_ref_buffer& result, + model_converter_ref& mc) { + + while (true) { + check_point(); + TRACE("nlsat_smt", m_solver->display(tout << "SMT:\n"); m_nl_g->display(tout << "\nNL:\n"); ); + goal_ref tmp_nl = alloc(goal, m, true, false); + model_converter_ref nl_mc; + proof_converter_ref nl_pc; + expr_dependency_ref nl_core(m); + result.reset(); + tmp_nl->copy_from(*m_nl_g.get()); + (*m_nl_tac)(tmp_nl, result, nl_mc, nl_pc, nl_core); + + if (is_decided_unsat(result)) { + TRACE("nlsat_smt", tout << "unsat\n";); + break; + } + if (!is_decided_sat(result)) { + TRACE("nlsat_smt", tout << "not a unit\n";); + break; + } + // extract evaluation on interface variables. + // assert booleans that evaluate to true. + // assert equalities between equal interface real variables. + + model_ref mdl_nl, mdl_smt; + if (mdl_nl.get()) { + model_converter2model(m, nl_mc.get(), mdl_nl); + update_eq_values(mdl_nl); + enforce_equalities(mdl_nl, m_nl_g); + + setup_assumptions(mdl_nl); + + TRACE("nlsat_smt", + model_smt2_pp(tout << "nl model\n", m, *mdl_nl.get(), 0); + m_solver->display(tout << "smt goal:\n"); tout << "\n";); + } + result.reset(); + lbool r = m_solver->check_sat(m_asms.size(), m_asms.c_ptr()); + if (r == l_false) { + // extract the core from the result + ptr_vector core; + m_solver->get_unsat_core(core); + if (core.empty()) { + goal_ref g = alloc(goal, m); + g->assert_expr(m.mk_false()); + result.push_back(g.get()); + break; + } + expr_ref_vector clause(m); + expr_ref fml(m); + expr* e; + for (unsigned i = 0; i < core.size(); ++i) { + clause.push_back(m.is_not(core[i], e)?e:m.mk_not(core[i])); + } + fml = m.mk_or(clause.size(), clause.c_ptr()); + m_nl_g->assert_expr(fml); + continue; + } + else if (r == l_true) { + m_solver->get_model(mdl_smt); + if (enforce_equalities(mdl_smt, m_nl_g)) { + // SMT enforced a new equality that wasn't true for nlsat. + continue; + } + TRACE("nlsat_smt", + m_fmc->display(tout << "joint state is sat\n"); + nl_mc->display(tout << "nl\n");); + if (mdl_nl.get()) { + merge_models(*mdl_nl.get(), mdl_smt); + } + mc = m_fmc.get(); + apply(mc, mdl_smt, 0); + mc = model2model_converter(mdl_smt.get()); + result.push_back(alloc(goal, m)); + } + else { + TRACE("nlsat_smt", tout << "unknown\n";); + } + break; + } + TRACE("nlsat_smt", display_result(tout, result);); + } + + + void setup_assumptions(model_ref& mdl) { + m_asms.reset(); + app_ref_vector const& fresh_preds = m_new_preds; + expr_ref tmp(m); + for (unsigned i = 0; i < fresh_preds.size(); ++i) { + expr* pred = fresh_preds[i]; + if (mdl->eval(pred, tmp)) { + polarity_t pol = m_polarities.find(pred); + // if assumptinon literals are used to satisfy NL state, + // we have to assume them when satisfying SMT state + if (pol != pol_neg && m.is_false(tmp)) { + m_asms.push_back(m.mk_not(pred)); + } + else if (pol != pol_pos && m.is_true(tmp)) { + m_asms.push_back(pred); + } + } + } + for (unsigned i = 0; i < m_eq_preds.size(); ++i) { + expr* pred = m_eq_preds[i].get(); + switch (m_eq_values[i]) { + case l_true: + m_asms.push_back(pred); + break; + case l_false: + m_asms.push_back(m.mk_not(pred)); + break; + default: + break; + } + } + TRACE("nlsat_smt", + tout << "assumptions:\n"; + for (unsigned i = 0; i < m_asms.size(); ++i) { + tout << mk_pp(m_asms[i].get(), m) << "\n"; + }); + } + + bool enforce_equalities(model_ref& mdl, goal_ref const& nl_g) { + TRACE("nlsat_smt", tout << "Enforce equalities " << m_interface_cache.size() << "\n";); + bool new_equality = false; + expr_ref_vector nums(m); + obj_map num2var; + obj_map::iterator it = m_interface_cache.begin(), end = m_interface_cache.end(); + for (; it != end; ++it) { + expr_ref r(m); + expr* v, *w, *pred; + w = it->m_value; + VERIFY(mdl->eval(w, r)); + TRACE("nlsat_smt", tout << mk_pp(w, m) << " |-> " << r << "\n";); + nums.push_back(r); + if (num2var.find(r, v)) { + if (!m_eq_pairs.find(v, w, pred)) { + pred = m.mk_fresh_const(0, m.mk_bool_sort()); + m_eq_preds.push_back(pred); + m_eq_values.push_back(l_true); + m_fmc->insert(to_app(pred)->get_decl()); + nl_g->assert_expr(m.mk_or(m.mk_not(pred), m.mk_eq(w, v))); + nl_g->assert_expr(m.mk_or(pred, m.mk_not(m.mk_eq(w, v)))); + m_solver->assert_expr(m.mk_iff(pred, m.mk_eq(w, v))); + new_equality = true; + m_eq_pairs.insert(v, w, pred); + } + else { + // interface equality is already enforced. + } + } + else { + num2var.insert(r, w); + } + } + return new_equality; + } + + void merge_models(model const& mdl_nl, model_ref& mdl_smt) { + expr_safe_replace num2num(m); + expr_ref result(m), val2(m); + expr_ref_vector args(m); + unsigned sz = mdl_nl.get_num_constants(); + for (unsigned i = 0; i < sz; ++i) { + func_decl* v = mdl_nl.get_constant(i); + if (u().is_real(v->get_range())) { + expr* val = mdl_nl.get_const_interp(v); + if (mdl_smt->eval(v, val2)) { + if (val != val2) { + num2num.insert(val2, val); + } + } + } + } + sz = mdl_smt->get_num_functions(); + for (unsigned i = 0; i < sz; ++i) { + func_decl* f = mdl_smt->get_function(i); + if (has_real(f)) { + unsigned arity = f->get_arity(); + func_interp* f1 = mdl_smt->get_func_interp(f); + func_interp* f2 = alloc(func_interp, m, f->get_arity()); + for (unsigned j = 0; j < f1->num_entries(); ++j) { + args.reset(); + func_entry const* entry = f1->get_entry(j); + for (unsigned k = 0; k < arity; ++k) { + translate(num2num, entry->get_arg(k), result); + args.push_back(result); + } + translate(num2num, entry->get_result(), result); + f2->insert_entry(args.c_ptr(), result); + } + translate(num2num, f1->get_else(), result); + f2->set_else(result); + mdl_smt->register_decl(f, f2); + } + } + mdl_smt->copy_const_interps(mdl_nl); + } + + bool has_real(func_decl* f) { + for (unsigned i = 0; i < f->get_arity(); ++i) { + if (u().is_real(f->get_domain(i))) return true; + } + return u().is_real(f->get_range()); + } + + void translate(expr_safe_replace& num2num, expr* e, expr_ref& result) { + result = 0; + if (e) { + num2num(e, result); + } + } + + void get_polarities(goal const& g) { + ptr_vector forms; + svector pols; + unsigned sz = g.size(); + for (unsigned i = 0; i < sz; ++i) { + forms.push_back(g.form(i)); + pols.push_back(pol_pos); + } + polarity_t p, q; + while (!forms.empty()) { + expr* e = forms.back(); + p = pols.back(); + forms.pop_back(); + pols.pop_back(); + if (m_polarities.find(e, q)) { + if (p == q || q == pol_dual) continue; + p = pol_dual; + } + TRACE("nlsat_smt_verbose", tout << mk_pp(e, m) << "\n";); + m_polarities.insert(e, p); + if (is_quantifier(e) || is_var(e)) { + throw tactic_exception("nl-purify tactic does not support quantifiers"); + } + SASSERT(is_app(e)); + app* a = to_app(e); + func_decl* f = a->get_decl(); + if (f->get_family_id() == m.get_basic_family_id() && p != pol_dual) { + switch(f->get_decl_kind()) { + case OP_NOT: + p = neg(p); + break; + case OP_AND: + case OP_OR: + break; + default: + p = pol_dual; + break; + } + } + else { + p = pol_dual; + } + for (unsigned i = 0; i < a->get_num_args(); ++i) { + forms.push_back(a->get_arg(i)); + pols.push_back(p); + } + } + } + + polarity_t neg(polarity_t p) { + switch (p) { + case pol_pos: return pol_neg; + case pol_neg: return pol_pos; + case pol_dual: return pol_dual; + } + return pol_dual; + } + + polarity_t join(polarity_t p, polarity_t q) { + if (p == q) return p; + return pol_dual; + } + + void rewrite_goal(rw& r, goal_ref const& g) { + r.reset(); + expr_ref new_curr(m); + proof_ref new_pr(m); + unsigned sz = g->size(); + for (unsigned i = 0; i < sz; i++) { + expr * curr = g->form(i); + r(curr, new_curr, new_pr); + if (m_produce_proofs) { + proof * pr = g->pr(i); + new_pr = m.mk_modus_ponens(pr, new_pr); + } + g->update(i, new_curr, new_pr, g->dep(i)); + } + } + + void remove_pure_arith(goal_ref const& g) { + obj_map is_pure; + unsigned sz = g->size(); + for (unsigned i = 0; i < sz; i++) { + expr * curr = g->form(i); + if (is_pure_arithmetic(is_pure, curr)) { + m_nl_g->assert_expr(curr, g->pr(i), g->dep(i)); + g->update(i, m.mk_true()); + } + } + } + + bool is_pure_arithmetic(obj_map& is_pure, expr* e0) { + ptr_vector todo; + todo.push_back(e0); + while (!todo.empty()) { + expr* e = todo.back(); + if (is_pure.contains(e)) { + todo.pop_back(); + continue; + } + if (!is_app(e)) { + todo.pop_back(); + is_pure.insert(e, false); + continue; + } + app* a = to_app(e); + bool pure = false, all_found = true, p; + pure |= (a->get_family_id() == u().get_family_id()) && u().is_real(a); + pure |= (m.is_eq(e) && u().is_real(a->get_arg(0))); + pure |= (a->get_family_id() == u().get_family_id()) && m.is_bool(a) && u().is_real(a->get_arg(0)); + pure |= (a->get_family_id() == m.get_basic_family_id()); + pure |= is_uninterp_const(a) && u().is_real(a); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + if (!is_pure.find(a->get_arg(i), p)) { + todo.push_back(a->get_arg(i)); + all_found = false; + } + else { + pure &= p; + } + } + if (all_found) { + is_pure.insert(e, pure); + todo.pop_back(); + } + } + return is_pure.find(e0); + } + +public: + + nl_purify_tactic(ast_manager & m, params_ref const& p): + m(m), + m_util(m), + m_params(p), + m_fmc(0), + m_cancel(false), + m_nl_tac(mk_nlsat_tactic(m, p)), + m_nl_g(0), + m_solver(mk_smt_solver(m, p, symbol::null)), + m_eq_preds(m), + m_new_reals(m), + m_new_preds(m), + m_asms(m) + {} + + virtual ~nl_purify_tactic() {} + + virtual void updt_params(params_ref const & p) { + m_params = p; + } + + virtual tactic * translate(ast_manager& m) { + return alloc(nl_purify_tactic, m, m_params); + } + + virtual void set_cancel(bool f) { + m_nl_tac->set_cancel(f); + if (f) { + m_solver->cancel(); + } + else { + m_solver->reset_cancel(); + } + m_cancel = f; + } + + virtual void cleanup() { + m_solver = mk_smt_solver(m, m_params, symbol::null); + m_nl_tac->cleanup(); + m_eq_preds.reset(); + m_eq_values.reset(); + m_new_reals.reset(); + m_new_preds.reset(); + m_eq_pairs.reset(); + m_polarities.reset(); + } + + virtual void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + + tactic_report report("qfufnl-purify", *g); + TRACE("nlsat_smt", g->display(tout);); + + m_produce_proofs = g->proofs_enabled(); + mc = 0; pc = 0; core = 0; + + fail_if_proof_generation("qfufnra-purify", g); + fail_if_unsat_core_generation("qfufnra-purify", g); + rw r(*this); + m_nl_g = alloc(goal, m, true, false); + m_fmc = alloc(filter_model_converter, m); + + // first hoist interface variables, + // then annotate subformulas by polarities, + // finally extract polynomial inequalities by + // creating a place-holder predicate inside the + // original goal and extracing pure nlsat clauses. + r.set_interface_var_mode(); + rewrite_goal(r, g); + remove_pure_arith(g); + get_polarities(*g.get()); + r.set_bool_mode(); + rewrite_goal(r, g); + + g->inc_depth(); + for (unsigned i = 0; i < g->size(); ++i) { + m_solver->assert_expr(g->form(i)); + } + g->inc_depth(); + solve(result, mc); + } +}; + + +tactic * mk_nl_purify_tactic(ast_manager& m, params_ref const& p) { + return alloc(nl_purify_tactic, m, p); +} diff --git a/src/tactic/nlsat_smt/nl_purify_tactic.h b/src/tactic/nlsat_smt/nl_purify_tactic.h new file mode 100644 index 000000000..9ffc99676 --- /dev/null +++ b/src/tactic/nlsat_smt/nl_purify_tactic.h @@ -0,0 +1,35 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + nl_purify_tactic.h + +Abstract: + + Tactic for purifying quantifier-free formulas that mix QF_NRA and other theories. + It is designed to allow cooprating between the nlsat solver and other theories + in a decoubled way. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-5-5. + +Revision History: + +--*/ +#ifndef _NL_PURIFY_TACTIC_H_ +#define _NL_PURIFY_TACTIC_H_ + +#include"params.h" +class ast_manager; +class tactic; + +tactic * mk_nl_purify_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* + ADD_TACTIC("nl-purify", "Decompose goal into pure NL-sat formula and formula over other theories.", "mk_nl_purify_tactic(m, p)") +*/ + +#endif + diff --git a/src/tactic/portfolio/default_tactic.cpp b/src/tactic/portfolio/default_tactic.cpp index 5a18e0830..72c486c24 100644 --- a/src/tactic/portfolio/default_tactic.cpp +++ b/src/tactic/portfolio/default_tactic.cpp @@ -30,20 +30,22 @@ Notes: #include"qffp_tactic.h" #include"qfaufbv_tactic.h" #include"qfauflia_tactic.h" +#include"qfufnra_tactic.h" tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), - cond(mk_is_qfaufbv_probe(), mk_qfaufbv_tactic(m), - cond(mk_is_qfauflia_probe(), mk_qfauflia_tactic(m), + cond(mk_is_qfaufbv_probe(), mk_qfaufbv_tactic(m), cond(mk_is_qflia_probe(), mk_qflia_tactic(m), + cond(mk_is_qfauflia_probe(), mk_qfauflia_tactic(m), cond(mk_is_qflra_probe(), mk_qflra_tactic(m), cond(mk_is_qfnra_probe(), mk_qfnra_tactic(m), cond(mk_is_qfnia_probe(), mk_qfnia_tactic(m), cond(mk_is_nra_probe(), mk_nra_tactic(m), cond(mk_is_lira_probe(), mk_lira_tactic(m, p), cond(mk_is_qffp_probe(), mk_qffp_tactic(m, p), - mk_smt_tactic()))))))))))), + cond(mk_is_qfufnra_probe(), mk_qfufnra_tactic(m, p), + mk_smt_tactic())))))))))))), p); return st; } diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index d8f23fc71..687771a30 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -34,6 +34,7 @@ Notes: #include"default_tactic.h" #include"ufbv_tactic.h" #include"qffp_tactic.h" +#include"qfufnra_tactic.h" #include"horn_tactic.h" #include"smt_solver.h" @@ -41,7 +42,7 @@ tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const if (logic=="QF_UF") return mk_qfuf_tactic(m, p); else if (logic=="QF_BV") - return mk_qfbv_tactic(m, p); + return mk_qfbv_tactic(m, p); else if (logic=="QF_IDL") return mk_qfidl_tactic(m, p); else if (logic=="QF_LIA") @@ -80,10 +81,12 @@ tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const return mk_ufbv_tactic(m, p); else if (logic=="QF_FP") return mk_qffp_tactic(m, p); - else if (logic == "QF_FPBV") + else if (logic == "QF_FPBV" || logic == "QF_BVFP") return mk_qffpbv_tactic(m, p); else if (logic=="HORN") return mk_horn_tactic(m, p); + else if (logic=="QF_UFNRA") + return mk_qfufnra_tactic(m, p); else return mk_default_tactic(m, p); } diff --git a/src/tactic/probe.cpp b/src/tactic/probe.cpp index 30a62fb5c..0fdd49a6b 100644 --- a/src/tactic/probe.cpp +++ b/src/tactic/probe.cpp @@ -283,13 +283,12 @@ struct is_non_qfbv_predicate { if (!m.is_bool(n) && !u.is_bv(n)) throw found(); family_id fid = n->get_family_id(); - if (fid == m.get_basic_family_id()) + if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) return; if (is_uninterp_const(n)) return; - throw found(); } }; diff --git a/src/tactic/replace_proof_converter.h b/src/tactic/replace_proof_converter.h index cb97810f8..7fee27612 100644 --- a/src/tactic/replace_proof_converter.h +++ b/src/tactic/replace_proof_converter.h @@ -30,11 +30,11 @@ class replace_proof_converter : public proof_converter { proof_ref_vector m_proofs; public: - replace_proof_converter(ast_manager& m): m(m), m_proofs(m) {} + replace_proof_converter(ast_manager& _m): m(_m), m_proofs(m) {} virtual ~replace_proof_converter() {} - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result); + virtual void operator()(ast_manager & _m, unsigned num_source, proof * const * source, proof_ref & result); virtual proof_converter * translate(ast_translation & translator); diff --git a/src/tactic/sls/bvsls_opt_engine.cpp b/src/tactic/sls/bvsls_opt_engine.cpp new file mode 100644 index 000000000..96fad9a5a --- /dev/null +++ b/src/tactic/sls/bvsls_opt_engine.cpp @@ -0,0 +1,367 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + bvsls_opt_engine.cpp + +Abstract: + + Optimization extensions to bvsls + +Author: + + Christoph (cwinter) 2014-03-28 + +Notes: + +--*/ +#include "nnf.h" +#include "bvsls_opt_engine.h" + +bvsls_opt_engine::bvsls_opt_engine(ast_manager & m, params_ref const & p) : + sls_engine(m, p), + m_hard_tracker(sls_engine::m_tracker), + m_obj_tracker(m, m_bv_util, m_mpz_manager, m_powers), + m_obj_evaluator(m, m_bv_util, m_obj_tracker, m_mpz_manager, m_powers) +{ + m_best_model = alloc(model, m); +} + +bvsls_opt_engine::~bvsls_opt_engine() +{ +} + +bvsls_opt_engine::optimization_result bvsls_opt_engine::optimize( + expr_ref const & objective, + model_ref initial_model, + bool _maximize) +{ + SASSERT(m_bv_util.is_bv(objective)); + TRACE("sls_opt", tout << "objective: " << (_maximize?"maximize":"minimize") << " " << + mk_ismt2_pp(objective, m()) << std::endl;); + m_hard_tracker.initialize(m_assertions); + setup_opt_tracker(objective, _maximize); + + if (initial_model.get() != 0) { + TRACE("sls_opt", tout << "Initial model provided: " << std::endl; + for (unsigned i = 0; i < initial_model->get_num_constants(); i++) { + func_decl * fd = initial_model->get_constant(i); + expr * val = initial_model->get_const_interp(fd); + tout << fd->get_name() << " := " << mk_ismt2_pp(val, m()) << std::endl; + }); + m_hard_tracker.set_model(initial_model); + m_evaluator.update_all(); + } + + optimization_result res(m_manager); + lbool is_sat = m_hard_tracker.is_sat() ? l_true : l_undef; + + TRACE("sls_opt", tout << "initial model is sat? " << is_sat << std::endl;); + + for (m_stats.m_restarts = 0; + m_stats.m_restarts < m_max_restarts; + m_stats.m_restarts++) + { + mpz old_best; + m_mpz_manager.set(old_best, m_best_model_score); + + if (is_sat != l_true) { + do { + checkpoint(); + + IF_VERBOSE(1, verbose_stream() << "Satisfying... restarts left:" << (m_max_restarts - m_stats.m_restarts) << std::endl;); + is_sat = search(); + + if (is_sat == l_undef) + m_hard_tracker.randomize(m_assertions); + } + while (is_sat != l_true && + m_stats.m_restarts++ < m_max_restarts); + } + + if (is_sat == l_true) { + IF_VERBOSE(1, verbose_stream() << "Optimizing... restarts left:" << (m_max_restarts - m_stats.m_restarts) << std::endl;); + res.is_sat = l_true; + m_obj_tracker.set_model(m_hard_tracker.get_model()); + m_obj_evaluator.update_all(); + expr_ref local_best = maximize(); + if ((_maximize && m_mpz_manager.gt(m_best_model_score, old_best)) || + (!_maximize && m_mpz_manager.lt(m_best_model_score, old_best))) + { + res.optimum = local_best; + } + } + + m_hard_tracker.randomize(m_assertions); + m_evaluator.update_all(); + is_sat = m_hard_tracker.is_sat() ? l_true : l_undef; + } + + TRACE("sls_opt", tout << "sat: " << res.is_sat << "; optimum: " << mk_ismt2_pp(res.optimum, m()) << std::endl;); + + return res; +} + +void bvsls_opt_engine::setup_opt_tracker(expr_ref const & objective, bool _max) +{ + expr_ref obj(m_manager); + obj = objective; + if (!_max) + obj = m_bv_util.mk_bv_neg(objective); + + m_obj_e = obj.get(); + m_obj_bv_sz = m_bv_util.get_bv_size(m_obj_e); + ptr_vector objs; + objs.push_back(m_obj_e); + m_obj_tracker.initialize(objs); +} + +expr_ref bvsls_opt_engine::maximize() +{ + SASSERT(m_hard_tracker.is_sat()); + + TRACE("sls_opt", tout << "Initial opt model:" << std::endl; m_obj_tracker.show_model(tout);); + + mpz score, old_score, max_score, new_value; + unsigned new_const = (unsigned)-1, new_bit = 0; + ptr_vector consts = m_obj_tracker.get_constants(); + move_type move; + m_mpz_manager.set(score, top_score()); + m_mpz_manager.set(max_score, m_powers(m_obj_bv_sz)); m_mpz_manager.dec(max_score); + + IF_VERBOSE(10, verbose_stream() << "Initial score: " << m_mpz_manager.to_string(score) << std::endl;); + + save_model(score); + + while (m_mpz_manager.lt(score, max_score) && check_restart(m_stats.m_moves)) + { + checkpoint(); + m_stats.m_moves++; + m_mpz_manager.set(old_score, score); + new_const = (unsigned)-1; + + mpz score(0); + m_mpz_manager.set(score, + find_best_move(consts, score, new_const, new_value, new_bit, move, max_score, m_obj_e)); + + if (new_const == static_cast(-1)) { + m_mpz_manager.set(score, old_score); + if (m_mpz_manager.gt(score, m_best_model_score)) + save_model(score); + if (!randomize_wrt_hard()) { + // Can't improve and can't randomize; can't do anything other than bail out. + TRACE("sls_opt", tout << "Got stuck; bailing out." << std::endl;); + IF_VERBOSE(10, verbose_stream() << "No local improvements possible." << std::endl;); + goto bailout; + } + m_mpz_manager.set(score, top_score()); + } + else { + m_stats.m_moves++; + TRACE("sls_opt", tout << "New optimum: " << m_mpz_manager.to_string(score) << std::endl;); + IF_VERBOSE(10, verbose_stream() << "New optimum: " << m_mpz_manager.to_string(score) << std::endl;); + func_decl * fd = consts[new_const]; + incremental_score(fd, new_value); + m_obj_evaluator.update(fd, new_value); + m_mpz_manager.set(score, top_score()); + } + } + +bailout: + m_mpz_manager.del(new_value); + + expr_ref res(m_manager); + res = m_bv_util.mk_numeral(m_best_model_score, m_obj_bv_sz); + return res; +} + +void bvsls_opt_engine::save_model(mpz const & score) { + model_ref mdl = m_hard_tracker.get_model(); + model_ref obj_mdl = m_obj_tracker.get_model(); + + for (unsigned i = 0; i < obj_mdl->get_num_constants(); i++) { + func_decl * fd = obj_mdl->get_constant(i); + expr * val = obj_mdl->get_const_interp(fd); + if (mdl->has_interpretation(fd)) { + if (mdl->get_const_interp(fd) != val) + TRACE("sls_opt", tout << "model disagreement on " << fd->get_name() << ": " << + mk_ismt2_pp(val, m()) << " != " << mk_ismt2_pp(mdl->get_const_interp(fd), m()) << std::endl;); + SASSERT(mdl->get_const_interp(fd) == val); + } + else + mdl->register_decl(fd, val); + } + + m_best_model = mdl; + m_mpz_manager.set(m_best_model_score, score); +} + +// checks whether the score outcome of a given move is better than the previous score +bool bvsls_opt_engine::what_if( + func_decl * fd, + const unsigned & fd_inx, + const mpz & temp, + mpz & best_score, + unsigned & best_const, + mpz & best_value) +{ +#if _EARLY_PRUNE_ + double r = incremental_score_prune(fd, temp); +#else + double r = incremental_score(fd, temp); +#endif + + if (r >= 1.0 && m_hard_tracker.is_sat()) { + m_obj_evaluator.update(fd, temp); + mpz cur_best(0); + m_mpz_manager.set(cur_best, top_score()); + + TRACE("sls_whatif", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << + " --> " << r << "; score=" << m_mpz_manager.to_string(cur_best) << std::endl;); + + if (m_mpz_manager.gt(cur_best, best_score)) { + m_mpz_manager.set(best_score, cur_best); + best_const = fd_inx; + m_mpz_manager.set(best_value, temp); + return true; + } + } + else + { + TRACE("sls_whatif_failed", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << + " --> unsatisfied hard constraints" << std::endl;); + } + + return false; +} + +mpz bvsls_opt_engine::find_best_move( + ptr_vector & to_evaluate, + mpz score, + unsigned & best_const, + mpz & best_value, + unsigned & new_bit, + move_type & move, + mpz const & max_score, + expr * objective) +{ + mpz old_value, temp; +#if _USE_MUL3_ || _USE_UNARY_MINUS_ + mpz temp2; +#endif + unsigned bv_sz; + mpz new_score; + m_mpz_manager.set(new_score, score); + + for (unsigned i = 0; i < to_evaluate.size() && m_mpz_manager.lt(new_score, max_score); i++) { + func_decl * fd = to_evaluate[i]; + sort * srt = fd->get_range(); + bv_sz = (m_manager.is_bool(srt)) ? 1 : m_bv_util.get_bv_size(srt); + m_mpz_manager.set(old_value, m_obj_tracker.get_value(fd)); + + // first try to flip every bit + for (unsigned j = 0; j < bv_sz && m_mpz_manager.lt(new_score, max_score); j++) { + // What would happen if we flipped bit #i ? + mk_flip(srt, old_value, j, temp); + + if (what_if(fd, i, temp, new_score, best_const, best_value)) { + new_bit = j; + move = MV_FLIP; + } + } + + if (m_bv_util.is_bv_sort(srt) && bv_sz > 1) { +#if _USE_ADDSUB_ + if (!m_mpz_manager.is_even(old_value)) { + // for odd values, try +1 + mk_inc(bv_sz, old_value, temp); + if (what_if(fd, i, temp, new_score, best_const, best_value)) + move = MV_INC; + } + else { + // for even values, try -1 + mk_dec(bv_sz, old_value, temp); + if (what_if(fd, i, temp, new_score, best_const, best_value)) + move = MV_DEC; + } +#endif + // try inverting + mk_inv(bv_sz, old_value, temp); + if (what_if(fd, i, temp, new_score, best_const, best_value)) + move = MV_INV; + +#if _USE_UNARY_MINUS_ + mk_inc(bv_sz, temp, temp2); + if (what_if(fd, i, temp2, new_score, best_const, best_value)) + move = MV_UMIN; +#endif + +#if _USE_MUL2DIV2_ + // try multiplication by 2 + mk_mul2(bv_sz, old_value, temp); + if (what_if(fd, i, temp, new_score, best_const, best_value)) + move = MV_MUL2; + +#if _USE_MUL3_ + // try multiplication by 3 + mk_add(bv_sz, old_value, temp, temp2); + if (what_if(fd, i, temp2, new_score, best_const, best_value)) + move = MV_MUL3; +#endif + + // try division by 2 + mk_div2(bv_sz, old_value, temp); + if (what_if(fd, i, temp, new_score, best_const, best_value)) + move = MV_DIV2; +#endif + } + + // reset to what it was before + //double check = + incremental_score(fd, old_value); + m_obj_evaluator.update(fd, old_value); + } + + m_mpz_manager.del(old_value); + m_mpz_manager.del(temp); +#if _USE_MUL3_ + m_mpz_manager.del(temp2); +#endif + + return new_score; +} + +bool bvsls_opt_engine::randomize_wrt_hard() { + ptr_vector consts = m_obj_tracker.get_constants(); + unsigned csz = consts.size(); + unsigned retry_count = csz; + + while (retry_count-- > 0) + { + + unsigned ri = (m_obj_tracker.get_random_uint((csz < 16) ? 4 : (csz < 256) ? 8 : (csz < 4096) ? 12 : (csz < 65536) ? 16 : 32)) % csz; + func_decl * random_fd = consts[ri]; // Random constant + + mpz random_val; // random value. + m_mpz_manager.set(random_val, m_obj_tracker.get_random(random_fd->get_range())); + + mpz old_value; + m_mpz_manager.set(old_value, m_obj_tracker.get_value(random_fd)); + + if (!m_mpz_manager.eq(random_val, old_value)) { + m_evaluator.update(random_fd, random_val); + + if (m_hard_tracker.is_sat()) { + TRACE("sls_opt", tout << "Randomizing " << random_fd->get_name() << " to " << + m_mpz_manager.to_string(random_val) << std::endl;); + m_obj_evaluator.update(random_fd, random_val); + return true; + } + else + m_evaluator.update(random_fd, old_value); + } + } + + return false; +} diff --git a/src/tactic/sls/bvsls_opt_engine.h b/src/tactic/sls/bvsls_opt_engine.h new file mode 100644 index 000000000..64a366d73 --- /dev/null +++ b/src/tactic/sls/bvsls_opt_engine.h @@ -0,0 +1,82 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + bvsls_opt_engine.h + +Abstract: + + Optimization extensions to bvsls + +Author: + + Christoph (cwinter) 2014-03-28 + +Notes: + +--*/ +#ifndef _BVSLS_OPT_ENGINE_H_ +#define _BVSLS_OPT_ENGINE_H_ + +#include "sls_engine.h" + +class bvsls_opt_engine : public sls_engine { + sls_tracker & m_hard_tracker; + sls_tracker m_obj_tracker; + sls_evaluator m_obj_evaluator; + model_ref m_best_model; + mpz m_best_model_score; + unsigned m_obj_bv_sz; + expr * m_obj_e; + +public: + bvsls_opt_engine(ast_manager & m, params_ref const & p); + ~bvsls_opt_engine(); + + class optimization_result { + public: + lbool is_sat; + expr_ref optimum; + optimization_result(ast_manager & m) : is_sat(l_undef), optimum(m) {} + optimization_result& operator=(optimization_result const& other) { + is_sat = other.is_sat; + optimum = other.optimum; + return *this; + } + optimization_result(optimization_result const& other): + is_sat(other.is_sat), + optimum(other.optimum) { + } + }; + + optimization_result optimize(expr_ref const & objective, model_ref initial_model = model_ref(), bool maximize=true); + + void get_model(model_ref & result) { result = m_best_model; } + +protected: + void setup_opt_tracker(expr_ref const & objective, bool _max); + expr_ref maximize(); + + bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, + mpz & best_score, unsigned & best_const, mpz & best_value); + + mpz find_best_move(ptr_vector & to_evaluate, mpz score, + unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move, + mpz const & max_score, expr * objective); + + mpz top_score(void) { + mpz res(0); + obj_hashtable const & top_exprs = m_obj_tracker.get_top_exprs(); + for (obj_hashtable::iterator it = top_exprs.begin(); + it != top_exprs.end(); + it++) + m_mpz_manager.add(res, m_obj_tracker.get_value(*it), res); + return res; + } + + void save_model(mpz const & score); + bool randomize_wrt_hard(); +}; + +#endif diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index fc7358f3d..a1c122dc2 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -80,6 +80,20 @@ void sls_engine::updt_params(params_ref const & _p) { NOT_IMPLEMENTED_YET(); } +void sls_engine::collect_statistics(statistics& st) const { + double seconds = m_stats.m_stopwatch.get_current_seconds(); + st.update("sls restarts", m_stats.m_restarts); + st.update("sls full evals", m_stats.m_full_evals); + st.update("sls incr evals", m_stats.m_incr_evals); + st.update("sls incr evals/sec", m_stats.m_incr_evals / seconds); + st.update("sls FLIP moves", m_stats.m_flips); + st.update("sls INC moves", m_stats.m_incs); + st.update("sls DEC moves", m_stats.m_decs); + st.update("sls INV moves", m_stats.m_invs); + st.update("sls moves", m_stats.m_moves); + st.update("sls moves/sec", m_stats.m_moves / seconds); +} + void sls_engine::checkpoint() { if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); diff --git a/src/tactic/sls/sls_engine.h b/src/tactic/sls/sls_engine.h index b056c438e..e0a28a796 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -26,6 +26,7 @@ Notes: #include"sls_tracker.h" #include"sls_evaluator.h" +#include"statistics.h" class sls_engine { public: @@ -100,7 +101,8 @@ public: void assert_expr(expr * e) { m_assertions.push_back(e); } - stats const & get_stats(void) { return m_stats; } + // stats const & get_stats(void) { return m_stats; } + void collect_statistics(statistics & st) const; void reset_statistics(void) { m_stats.reset(); } bool full_eval(model & mdl); diff --git a/src/tactic/sls/sls_evaluator.h b/src/tactic/sls/sls_evaluator.h index 61afb7457..ed4717641 100644 --- a/src/tactic/sls/sls_evaluator.h +++ b/src/tactic/sls/sls_evaluator.h @@ -120,6 +120,14 @@ public: } break; } + case OP_ITE: { + SASSERT(n_args = 3); + if (m_mpz_manager.is_one(m_tracker.get_value(args[0]))) + m_mpz_manager.set(result, m_tracker.get_value(args[1])); + else + m_mpz_manager.set(result, m_tracker.get_value(args[2])); + break; + } default: NOT_IMPLEMENTED_YET(); } diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index b06a047f7..4df74f20a 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -88,18 +88,7 @@ public: } virtual void collect_statistics(statistics & st) const { - sls_engine::stats const & stats = m_engine->get_stats(); - double seconds = stats.m_stopwatch.get_current_seconds(); - st.update("sls restarts", stats.m_restarts); - st.update("sls full evals", stats.m_full_evals); - st.update("sls incr evals", stats.m_incr_evals); - st.update("sls incr evals/sec", stats.m_incr_evals / seconds); - st.update("sls FLIP moves", stats.m_flips); - st.update("sls INC moves", stats.m_incs); - st.update("sls DEC moves", stats.m_decs); - st.update("sls INV moves", stats.m_invs); - st.update("sls moves", stats.m_moves); - st.update("sls moves/sec", stats.m_moves / seconds); + m_engine->collect_statistics(st); } virtual void reset_statistics() { diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index 87c0f962c..2e041ae97 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -21,7 +21,6 @@ Notes: #define _SLS_TRACKER_H_ #include - #include"for_each_expr.h" #include"ast_smt2_pp.h" #include"bv_decl_plugin.h" @@ -164,6 +163,19 @@ public: return m_top_sum; } + inline obj_hashtable const & get_top_exprs() { + return m_top_expr; + } + + inline bool is_sat() { + for (obj_hashtable::iterator it = m_top_expr.begin(); + it != m_top_expr.end(); + it++) + if (!m_mpz_manager.is_one(get_value(*it))) + return false; + return true; + } + inline void set_value(expr * n, const mpz & r) { SASSERT(m_scores.contains(n)); m_mpz_manager.set(m_scores.find(n).value, r); @@ -515,6 +527,28 @@ public: } } + void set_model(model_ref const & mdl) { + for (unsigned i = 0; i < mdl->get_num_constants(); i++) { + func_decl * fd = mdl->get_constant(i); + expr * val = mdl->get_const_interp(fd); + if (m_entry_points.contains(fd)) { + if (m_manager.is_bool(val)) { + set_value(fd, m_manager.is_true(val) ? m_mpz_manager.mk_z(1) : m_mpz_manager.mk_z(0)); + } + else if (m_bv_util.is_numeral(val)) { + rational r_val; + unsigned bv_sz; + m_bv_util.is_numeral(val, r_val, bv_sz); + mpq q = r_val.to_mpq(); + SASSERT(m_mpz_manager.is_one(q.denominator())); + set_value(fd, q.numerator()); + } + else + NOT_IMPLEMENTED_YET(); + } + } + } + model_ref get_model() { model_ref res = alloc(model, m_manager); unsigned sz = get_num_constants(); @@ -660,6 +694,9 @@ public: m_scores.find(n).has_pos_occ = 1; } } + else if (m_bv_util.is_bv(n)) { + /* CMW: I need this for optimization. Safe to ignore? */ + } else NOT_IMPLEMENTED_YET(); } diff --git a/src/tactic/smtlogics/qfbv_tactic.cpp b/src/tactic/smtlogics/qfbv_tactic.cpp index ac53ca0c8..4e017e9e6 100644 --- a/src/tactic/smtlogics/qfbv_tactic.cpp +++ b/src/tactic/smtlogics/qfbv_tactic.cpp @@ -31,12 +31,12 @@ Notes: #define MEMLIMIT 300 -tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { - params_ref main_p; - main_p.set_bool("elim_and", true); - main_p.set_bool("push_ite_bv", true); - main_p.set_bool("blast_distinct", true); +tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p) { + params_ref solve_eq_p; + // conservative guassian elimination. + solve_eq_p.set_uint("solve_eqs_max_occs", 2); + params_ref simp2_p = p; simp2_p.set_bool("som", true); simp2_p.set_bool("pull_cheap_ite", true); @@ -46,6 +46,40 @@ tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { simp2_p.set_bool("flat", true); // required by som simp2_p.set_bool("hoist_mul", false); // required by som + + params_ref hoist_p; + hoist_p.set_bool("hoist_mul", true); + hoist_p.set_bool("som", false); + + return + and_then( + mk_simplify_tactic(m), + mk_propagate_values_tactic(m), + using_params(mk_solve_eqs_tactic(m), solve_eq_p), + mk_elim_uncnstr_tactic(m), + if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), + using_params(mk_simplify_tactic(m), simp2_p), + // + // Z3 can solve a couple of extra benchmarks by using hoist_mul + // but the timeout in SMT-COMP is too small. + // Moreover, it impacted negatively some easy benchmarks. + // We should decide later, if we keep it or not. + // + using_params(mk_simplify_tactic(m), hoist_p), + mk_max_bv_sharing_tactic(m)); +} + +static tactic * main_p(tactic* t) { + params_ref p; + p.set_bool("elim_and", true); + p.set_bool("push_ite_bv", true); + p.set_bool("blast_distinct", true); + return using_params(t, p); +} + + +tactic * mk_qfbv_tactic(ast_manager& m, params_ref const & p, tactic* sat, tactic* smt) { + params_ref local_ctx_p = p; local_ctx_p.set_bool("local_ctx", true); @@ -59,62 +93,45 @@ tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { ctx_simp_p.set_uint("max_depth", 32); ctx_simp_p.set_uint("max_steps", 50000000); - params_ref hoist_p; - hoist_p.set_bool("hoist_mul", true); - hoist_p.set_bool("som", false); - - params_ref solve_eq_p; - // conservative guassian elimination. - solve_eq_p.set_uint("solve_eqs_max_occs", 2); params_ref big_aig_p; big_aig_p.set_bool("aig_per_assertion", false); - - tactic * preamble_st = and_then(and_then(mk_simplify_tactic(m), - mk_propagate_values_tactic(m), - using_params(mk_solve_eqs_tactic(m), solve_eq_p), - mk_elim_uncnstr_tactic(m), - if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), - using_params(mk_simplify_tactic(m), simp2_p)), - // Z3 can solve a couple of extra benchmarks by using hoist_mul - // but the timeout in SMT-COMP is too small. - // Moreover, it impacted negatively some easy benchmarks. - // We should decide later, if we keep it or not. - using_params(mk_simplify_tactic(m), hoist_p), - mk_max_bv_sharing_tactic(m)); -#ifdef USE_OLD_SAT_SOLVER - tactic * new_sat = and_then(mk_simplify_tactic(m), - mk_smt_tactic()); -#else - tactic * new_sat = cond(mk_or(mk_produce_proofs_probe(), mk_produce_unsat_cores_probe()), - and_then(mk_simplify_tactic(m), - mk_smt_tactic()), - mk_sat_tactic(m)); -#endif + tactic* preamble_st = mk_qfbv_preamble(m, p); + + tactic * st = main_p(and_then(preamble_st, + // If the user sets HI_DIV0=false, then the formula may contain uninterpreted function + // symbols. In this case, we should not use + cond(mk_is_qfbv_probe(), + cond(mk_is_qfbv_eq_probe(), + and_then(mk_bv1_blaster_tactic(m), + using_params(smt, solver_p)), + and_then(mk_bit_blaster_tactic(m), + when(mk_lt(mk_memory_probe(), mk_const_probe(MEMLIMIT)), + and_then(using_params(and_then(mk_simplify_tactic(m), + mk_solve_eqs_tactic(m)), + local_ctx_p), + if_no_proofs(cond(mk_produce_unsat_cores_probe(), + mk_aig_tactic(), + using_params(mk_aig_tactic(), + big_aig_p))))), + sat)), + smt))); - tactic * st = using_params(and_then(preamble_st, - // If the user sets HI_DIV0=false, then the formula may contain uninterpreted function - // symbols. In this case, we should not use - cond(mk_is_qfbv_probe(), - cond(mk_is_qfbv_eq_probe(), - and_then(mk_bv1_blaster_tactic(m), - using_params(mk_smt_tactic(), solver_p)), - and_then(mk_bit_blaster_tactic(m), - when(mk_lt(mk_memory_probe(), mk_const_probe(MEMLIMIT)), - and_then(using_params(and_then(mk_simplify_tactic(m), - mk_solve_eqs_tactic(m)), - local_ctx_p), - if_no_proofs(cond(mk_produce_unsat_cores_probe(), - mk_aig_tactic(), - using_params(mk_aig_tactic(), - big_aig_p))))), - new_sat)), - mk_smt_tactic())), - main_p); - st->updt_params(p); return st; + +} + + +tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { + + tactic * new_sat = cond(mk_produce_proofs_probe(), + and_then(mk_simplify_tactic(m), mk_smt_tactic()), + mk_sat_tactic(m)); + + return mk_qfbv_tactic(m, p, new_sat, mk_smt_tactic()); + } diff --git a/src/tactic/smtlogics/qfbv_tactic.h b/src/tactic/smtlogics/qfbv_tactic.h index 7210f6f25..98b3e2289 100644 --- a/src/tactic/smtlogics/qfbv_tactic.h +++ b/src/tactic/smtlogics/qfbv_tactic.h @@ -24,8 +24,13 @@ class ast_manager; class tactic; tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p = params_ref()); + /* ADD_TACTIC("qfbv", "builtin strategy for solving QF_BV problems.", "mk_qfbv_tactic(m, p)") */ +tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p); + +tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p, tactic* sat, tactic* smt); + #endif diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index 6fbf34255..00f6480d8 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -56,7 +56,7 @@ struct quasi_pb_probe : public probe { } }; -probe * mk_quasi_pb_probe() { +probe * mk_is_quasi_pb_probe() { return mk_and(mk_not(mk_is_unbounded_probe()), alloc(quasi_pb_probe)); } @@ -100,9 +100,11 @@ static tactic * mk_bv2sat_tactic(ast_manager & m) { #define SMALL_SIZE 80000 static tactic * mk_pb_tactic(ast_manager & m) { - params_ref pb2bv_p; - pb2bv_p.set_bool("ite_extra", true); + params_ref pb2bv_p; pb2bv_p.set_uint("pb2bv_all_clauses_limit", 8); + + params_ref bv2sat_p; + bv2sat_p.set_bool("ite_extra", true); return and_then(fail_if_not(mk_is_pb_probe()), fail_if(mk_produce_proofs_probe()), @@ -113,14 +115,16 @@ static tactic * mk_pb_tactic(ast_manager & m) { mk_fail_if_undecided_tactic()), and_then(using_params(mk_pb2bv_tactic(m), pb2bv_p), fail_if_not(mk_is_qfbv_probe()), - mk_bv2sat_tactic(m)))); + using_params(mk_bv2sat_tactic(m), bv2sat_p)))); } static tactic * mk_lia2sat_tactic(ast_manager & m) { params_ref pb2bv_p; - pb2bv_p.set_bool("ite_extra", true); pb2bv_p.set_uint("pb2bv_all_clauses_limit", 8); + + params_ref bv2sat_p; + bv2sat_p.set_bool("ite_extra", true); return and_then(fail_if(mk_is_unbounded_probe()), fail_if(mk_produce_proofs_probe()), @@ -130,7 +134,7 @@ static tactic * mk_lia2sat_tactic(ast_manager & m) { mk_lia2pb_tactic(m), using_params(mk_pb2bv_tactic(m), pb2bv_p), fail_if_not(mk_is_qfbv_probe()), - mk_bv2sat_tactic(m)); + using_params(mk_bv2sat_tactic(m), bv2sat_p)); } // Try to find a model for an unbounded ILP problem. @@ -208,7 +212,7 @@ tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(preamble_st, or_else(mk_ilp_model_finder_tactic(m), mk_pb_tactic(m), - and_then(fail_if_not(mk_quasi_pb_probe()), + and_then(fail_if_not(mk_is_quasi_pb_probe()), using_params(mk_lia2sat_tactic(m), quasi_pb_p), mk_fail_if_undecided_tactic()), mk_bounded_tactic(m), diff --git a/src/tactic/smtlogics/qflia_tactic.h b/src/tactic/smtlogics/qflia_tactic.h index 9ddaf1f88..47a2108ae 100644 --- a/src/tactic/smtlogics/qflia_tactic.h +++ b/src/tactic/smtlogics/qflia_tactic.h @@ -28,4 +28,11 @@ tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p = params_ref()); ADD_TACTIC("qflia", "builtin strategy for solving QF_LIA problems.", "mk_qflia_tactic(m, p)") */ + +probe * mk_is_quasi_pb_probe(); + +/* + ADD_PROBE("is-quasi-pb", "true if the goal is quasi-pb.", "mk_is_quasi_pb_probe()") +*/ + #endif diff --git a/src/tactic/smtlogics/qfufnra_tactic.cpp b/src/tactic/smtlogics/qfufnra_tactic.cpp new file mode 100644 index 000000000..4ca241e19 --- /dev/null +++ b/src/tactic/smtlogics/qfufnra_tactic.cpp @@ -0,0 +1,50 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + qfufnra_tactic.cpp + +Abstract: + + Tactic for QF_UFNRA + +Author: + + Nikolaj (nbjorner) 2015-05-05 + +Notes: + +--*/ +#include"tactical.h" +#include"simplify_tactic.h" +#include"propagate_values_tactic.h" +#include"nl_purify_tactic.h" +#include"qfufnra_tactic.h" +#include"purify_arith_tactic.h" +#include"solve_eqs_tactic.h" +#include"elim_term_ite_tactic.h" +#include"elim_uncnstr_tactic.h" +#include"simplify_tactic.h" +#include"nnf_tactic.h" +#include"tseitin_cnf_tactic.h" + +tactic * mk_qfufnra_tactic(ast_manager & m, params_ref const& p) { + params_ref main_p = p; + main_p.set_bool("elim_and", true); + main_p.set_bool("blast_distinct", true); + + return and_then(and_then(using_params(mk_simplify_tactic(m, p), main_p), + mk_purify_arith_tactic(m, p), + mk_propagate_values_tactic(m, p), + mk_solve_eqs_tactic(m, p), + mk_elim_uncnstr_tactic(m, p)), + and_then(mk_elim_term_ite_tactic(m, p), + mk_solve_eqs_tactic(m, p), + using_params(mk_simplify_tactic(m, p), main_p), + mk_tseitin_cnf_core_tactic(m, p), + using_params(mk_simplify_tactic(m, p), main_p), + mk_nl_purify_tactic(m, p))); +} + + diff --git a/src/tactic/smtlogics/qfufnra_tactic.h b/src/tactic/smtlogics/qfufnra_tactic.h new file mode 100644 index 000000000..a2ac9b1ea --- /dev/null +++ b/src/tactic/smtlogics/qfufnra_tactic.h @@ -0,0 +1,31 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + qfufnra_tactic.h + +Abstract: + + Tactic for QF_UFNRA + +Author: + + Leonardo (leonardo) 2012-02-28 + +Notes: + +--*/ +#ifndef _QFUFNRA_TACTIC_ +#define _QFUFNRA_TACTIC_ + +#include"params.h" +class ast_manager; +class tactic; + +tactic * mk_qfufnra_tactic(ast_manager & m, params_ref const & p = params_ref()); +/* + ADD_TACTIC("qfufnra", "builtin strategy for solving QF_UNFRA problems.", "mk_qfufnra_tactic(m, p)") +*/ + +#endif diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 198872cf5..5025b4449 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -207,7 +207,7 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, proof_ref & pr, expr_d TRACE("tactic_mc", mc->display(tout);); TRACE("tactic_check_sat", tout << "r.size(): " << r.size() << "\n"; - for (unsigned i = 0; i < r.size(); i++) r[0]->display(tout);); + for (unsigned i = 0; i < r.size(); i++) r[i]->display(tout);); if (is_decided_sat(r)) { if (models_enabled) { diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 8b4f9f576..cab93f1ea 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -95,10 +95,11 @@ public: // translate tactic to the given manager virtual tactic * translate(ast_manager & m) = 0; -private: +protected: friend class nary_tactical; friend class binary_tactical; friend class unary_tactical; + friend class nl_purify_tactic; virtual void set_cancel(bool f) {} diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index cfb4ec194..149cbc272 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -283,6 +283,10 @@ tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8, t9, t10)); } +tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10, tactic * t11) { + return and_then(t1, and_then(t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)); +} + tactic * and_then(unsigned num, tactic * const * ts) { SASSERT(num > 0); unsigned i = num - 1; @@ -705,8 +709,6 @@ public: tactic_ref_vector ts2; goal_ref_vector g_copies; - ast_manager & m = in->m(); - for (unsigned i = 0; i < r1_size; i++) { ast_manager * new_m = alloc(ast_manager, m, !m.proof_mode()); managers.push_back(new_m); diff --git a/src/tactic/tactical.h b/src/tactic/tactical.h index 4ceff2031..e291e6864 100644 --- a/src/tactic/tactical.h +++ b/src/tactic/tactical.h @@ -32,6 +32,7 @@ tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9); tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10); +tactic * and_then(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5, tactic * t6, tactic * t7, tactic * t8, tactic * t9, tactic * t10, tactic * t11); tactic * or_else(unsigned num, tactic * const * ts); tactic * or_else(tactic * t1, tactic * t2); diff --git a/src/test/api.cpp b/src/test/api.cpp index a09371f01..a463bbe47 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifdef _WINDOWS #include "z3.h" #include "z3_private.h" @@ -447,9 +453,28 @@ void test_bvneg() { Z3_del_context(ctx); } +static bool cb_called = false; +static void my_cb(Z3_context, Z3_error_code) { + cb_called = true; +} + +static void test_mk_distinct() { + Z3_config cfg = Z3_mk_config(); + Z3_context ctx = Z3_mk_context(cfg); + Z3_set_error_handler(ctx, my_cb); + + Z3_sort bv8 = Z3_mk_bv_sort(ctx, 8); + Z3_sort bv32 = Z3_mk_bv_sort(ctx, 32); + Z3_ast args[] = { Z3_mk_int64(ctx, 0, bv8), Z3_mk_int64(ctx, 0, bv32) }; + Z3_ast d = Z3_mk_distinct(ctx, 2, args); + SASSERT(cb_called); + +} + void tst_api() { test_apps(); test_bvneg(); + test_mk_distinct(); // bv_invariant(); } #else diff --git a/src/test/api_bug.cpp b/src/test/api_bug.cpp index 4519b2352..b88840eb6 100644 --- a/src/test/api_bug.cpp +++ b/src/test/api_bug.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include #include"z3.h" diff --git a/src/test/arith_rewriter.cpp b/src/test/arith_rewriter.cpp index 9eb9e559b..5ed7c3501 100644 --- a/src/test/arith_rewriter.cpp +++ b/src/test/arith_rewriter.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "arith_rewriter.h" #include "bv_decl_plugin.h" #include "ast_pp.h" diff --git a/src/test/arith_simplifier_plugin.cpp b/src/test/arith_simplifier_plugin.cpp index 59f4996ce..8464fd830 100644 --- a/src/test/arith_simplifier_plugin.cpp +++ b/src/test/arith_simplifier_plugin.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "arith_eq_solver.h" #include "smt_params.h" diff --git a/src/test/bits.cpp b/src/test/bits.cpp index 7254a177d..f32871a5a 100644 --- a/src/test/bits.cpp +++ b/src/test/bits.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + // Test some bit hacks #include"util.h" #include"debug.h" diff --git a/src/test/bv_simplifier_plugin.cpp b/src/test/bv_simplifier_plugin.cpp index 77e57f1f5..dc930bb82 100644 --- a/src/test/bv_simplifier_plugin.cpp +++ b/src/test/bv_simplifier_plugin.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "bv_simplifier_plugin.h" #include "arith_decl_plugin.h" #include "ast_pp.h" diff --git a/src/test/check_assumptions.cpp b/src/test/check_assumptions.cpp index 872af714c..fa8327cd4 100644 --- a/src/test/check_assumptions.cpp +++ b/src/test/check_assumptions.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "memory_manager.h" #include "smt_params.h" #include "ast.h" diff --git a/src/test/datalog_parser.cpp b/src/test/datalog_parser.cpp index e23650e3b..a927be29a 100644 --- a/src/test/datalog_parser.cpp +++ b/src/test/datalog_parser.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "datalog_parser.h" #include "ast_pp.h" #include "arith_decl_plugin.h" diff --git a/src/test/ddnf.cpp b/src/test/ddnf.cpp new file mode 100644 index 000000000..8620bd441 --- /dev/null +++ b/src/test/ddnf.cpp @@ -0,0 +1,206 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "ddnf.h" +#include "tbv.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* +TBD: count number of nodes, number of operations accross all insertions +*/ + +void read_nums(std::istream& is, unsigned & x, unsigned& y) { + x = 0; y = 0; + is >> x; + is >> y; + std::string line; + std::getline(is, line); +} + +static char const* g_file = 0; + + +void create_forwarding( + char const* file, + datalog::ddnf_core& ddnf, + ptr_vector& tbvs, + vector& fwd_indices) { + + IF_VERBOSE(1, verbose_stream() << "creating (and forgetting) forwarding index\n";); + std::ifstream is(file); + if (is.bad() || is.fail()) { + std::cout << "could not load " << file << "\n"; + exit(0); + } + + std::string line; + unsigned W, M; + read_nums(is, W, M); + tbv_manager& tbvm = ddnf.get_tbv_manager(); + tbv* tX = tbvm.allocateX(); + unsigned_vector forwarding_set; + for (unsigned r = 0; r < M; ++r) { + unsigned P, K; + read_nums(is, K, P); + ddnf.reset_accumulate(); + unsigned p; + fwd_indices.push_back(unsigned_vector()); + unsigned_vector& forwarding_index = fwd_indices.back(); + forwarding_index.resize(ddnf.size()); + for (unsigned g = 0; g < K; ++g) { + is >> p; + std::getline(is, line); + tbv* t = tbvm.allocate(line.c_str()); + if (p > P) { + std::cout << "port number " << p << " too big " << P << "\n"; + tbvm.display(std::cout, *t) << " " << line << "\n"; + exit(0); + } + forwarding_set.reset(); + ddnf.accumulate(*t, forwarding_set); + for (unsigned i = 0; i < forwarding_set.size(); ++i) { + forwarding_index[forwarding_set[i]] = p; + } + tbvs.push_back(t); + if (p == 0 && tbvm.equals(*t, *tX)) break; + } + } + tbvm.deallocate(tX); +} + +datalog::ddnf_core* populate_ddnf(char const* file, ptr_vector& tbvs) { + + IF_VERBOSE(1, verbose_stream() << "populate ddnf\n";); + + std::ifstream is(file); + if (is.bad() || is.fail()) { + std::cout << "could not load " << file << "\n"; + exit(0); + } + + std::string line; + unsigned W, M; + read_nums(is, W, M); + datalog::ddnf_core* ddnf = alloc(datalog::ddnf_core, W); + tbv_manager& tbvm = ddnf->get_tbv_manager(); + tbv* tX = tbvm.allocateX(); + for (unsigned r = 0; r < M; ++r) { + unsigned P, K; + read_nums(is, K, P); + IF_VERBOSE(1, verbose_stream() << K << " " << P << "\n";); + unsigned p; + for (unsigned g = 0; g < K; ++g) { + is >> p; + std::getline(is, line); + tbv* t = tbvm.allocate(line.c_str()); + ddnf->insert(*t); + IF_VERBOSE(2, tbvm.display(verbose_stream() << line << " ", *t) << "\n";); + tbvs.push_back(t); + if (p > P) { + std::cout << "port number " << p << " too big " << P << "\n"; + tbvm.display(std::cout, *t) << " " << line << "\n"; + exit(0); + } + if (p == 0 && tbvm.equals(*t, *tX)) break; + // std::cout << ddnf->well_formed() << "\n"; + } + } + + tbvm.deallocate(tX); + + return ddnf; +} + + +static void read_args(char ** argv, int argc, int& i) { + if (argc = i + 2) { + g_file = argv[i + 1]; + ++i; + return; + } + + if (!g_file) { + std::cout << "Need routing table file as argument. Arguments provided: "; + for (int j = i; j < argc; ++j) { + std::cout << argv[j] << " "; + } + std::cout << "\n"; + exit(0); + } + +} + +typedef std::pair u_pair; + +struct uu_eq { bool operator()(u_pair u1, u_pair u2) const { return u1 == u2; } }; + +typedef map, uu_eq > pair_table; + +static unsigned refine_forwarding( + unsigned_vector& p, + unsigned_vector const& q) { + unsigned sz = p.size(); + unsigned n = 0, m = 0; + pair_table tbl; + for (unsigned i = 0; i < sz; ++i) { + u_pair pr = std::make_pair(p[i], q[i]); + if (tbl.find(pr, m)) { + p[i] = m; + } + else { + p[i] = n; + tbl.insert(pr, n++); + } + } + return n; +} + +static void refine_forwarding( + datalog::ddnf_core& ddnf, + vector const& fwd_indices) { + unsigned_vector roots; + roots.resize(ddnf.size()); + for (unsigned i = 0; i < roots.size(); ++i) { + roots[i] = 0; + } + unsigned max_class = 1; + for (unsigned i = 0; i < fwd_indices.size(); ++i) { + unsigned_vector const& fwd = fwd_indices[i]; + max_class = refine_forwarding(roots, fwd); + } + std::cout << "num classes: " << max_class << "\n"; +} + +void tst_ddnf(char ** argv, int argc, int& i) { + read_args(argv, argc, i); + ptr_vector tbvs; + datalog::ddnf_core* ddnf = populate_ddnf(g_file, tbvs); + IF_VERBOSE(1, ddnf->display(verbose_stream());); + vector fwd_indices; + create_forwarding(g_file, *ddnf, tbvs, fwd_indices); + refine_forwarding(*ddnf, fwd_indices); + std::cout << "resulting size: " << ddnf->size() << "\n"; + ddnf->display_statistics(std::cout); + IF_VERBOSE(1, ddnf->display(verbose_stream()); + verbose_stream() << ddnf->well_formed() << "\n";); + + tbv_manager& tbvm = ddnf->get_tbv_manager(); + for (unsigned i = 0; i < tbvs.size(); ++i) { + tbvm.deallocate(tbvs[i]); + } + dealloc(ddnf); +} + + + + diff --git a/src/test/dl_context.cpp b/src/test/dl_context.cpp index 5c70aa8b5..09db4bde2 100644 --- a/src/test/dl_context.cpp +++ b/src/test/dl_context.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "datalog_parser.h" #include "ast_pp.h" #include "arith_decl_plugin.h" diff --git a/src/test/dl_product_relation.cpp b/src/test/dl_product_relation.cpp index 357ccd604..c375587fb 100644 --- a/src/test/dl_product_relation.cpp +++ b/src/test/dl_product_relation.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifdef _WINDOWS #include "dl_context.h" #include "dl_register_engine.h" diff --git a/src/test/dl_query.cpp b/src/test/dl_query.cpp index bb991c65f..9e0f285c7 100644 --- a/src/test/dl_query.cpp +++ b/src/test/dl_query.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "datalog_parser.h" #include "ast_pp.h" #include "dl_table_relation.h" @@ -58,8 +64,12 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, ctx_q.updt_params(params); { parser* p = parser::create(ctx_q,m); - TRUSTME( p->parse_file(problem_file) ); + bool ok = p->parse_file(problem_file); dealloc(p); + if (!ok) { + std::cout << "Could not parse: " << problem_file << "\n"; + return; + } } relation_manager & rel_mgr_q = ctx_b.get_rel_context()->get_rmanager(); @@ -143,8 +153,12 @@ void dl_query_test_wpa(smt_params & fparams, params_ref& params) { ctx.updt_params(params); { wpa_parser* p = wpa_parser::create(ctx, m); - TRUSTME( p->parse_directory(problem_dir) ); + bool ok = p->parse_directory(problem_dir); dealloc(p); + if (!ok) { + std::cout << "Could not parse: " << problem_dir << "\n"; + return; + } } const unsigned attempts = 10; @@ -213,8 +227,12 @@ void tst_dl_query() { ctx_base.updt_params(params); { parser* p = parser::create(ctx_base,m); - TRUSTME( p->parse_file(problem_file) ); + bool ok = p->parse_file(problem_file); dealloc(p); + if (!ok) { + std::cout << "Could not parse: " << problem_file << "\n"; + return; + } } ctx_base.get_rel_context()->saturate(); diff --git a/src/test/dl_relation.cpp b/src/test/dl_relation.cpp index bb1c8614c..c2ce807a9 100644 --- a/src/test/dl_relation.cpp +++ b/src/test/dl_relation.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifdef _WINDOWS #include "dl_context.h" #include "dl_register_engine.h" diff --git a/src/test/dl_table.cpp b/src/test/dl_table.cpp index d24200f9b..b14988f11 100644 --- a/src/test/dl_table.cpp +++ b/src/test/dl_table.cpp @@ -1,4 +1,8 @@ -#ifdef _WINDOWS +/*++ +Copyright (c) 2015 Microsoft Corporation +--*/ +#if defined(_WINDOWS) || defined(_CYGWIN) + #include "dl_context.h" #include "dl_table.h" #include "dl_register_engine.h" @@ -91,9 +95,78 @@ void test_dl_bitvector_table() { test_table(mk_bv_table); } +void test_table_min() { + std::cout << "----- test_table_min -----\n"; + datalog::table_signature sig; + sig.push_back(2); + sig.push_back(4); + sig.push_back(8); + smt_params params; + ast_manager ast_m; + datalog::register_engine re; + datalog::context ctx(ast_m, re, params); + datalog::relation_manager & m = ctx.get_rel_context()->get_rmanager(); + + m.register_plugin(alloc(datalog::bitvector_table_plugin, m)); + + datalog::table_base* tbl = mk_bv_table(m, sig); + datalog::table_base& table = *tbl; + + datalog::table_fact row, row1, row2, row3; + row.push_back(1); + row.push_back(2); + row.push_back(5); + + // Group (1,2,*) + row1 = row; + row[2] = 6; + row2 = row; + row[2] = 5; + row3 = row; + + table.add_fact(row1); + table.add_fact(row2); + table.add_fact(row3); + + // Group (1,3,*) + row[1] = 3; + row1 = row; + row[2] = 7; + row2 = row; + row[2] = 4; + row3 = row; + + table.add_fact(row1); + table.add_fact(row2); + table.add_fact(row3); + + table.display(std::cout); + + unsigned_vector group_by(2); + group_by[0] = 0; + group_by[1] = 1; + + datalog::table_min_fn * min_fn = m.mk_min_fn(table, group_by, 2); + datalog::table_base * min_tbl = (*min_fn)(table); + + min_tbl->display(std::cout); + + row[1] = 2; + row[2] = 5; + SASSERT(min_tbl->contains_fact(row)); + + row[1] = 3; + row[2] = 4; + SASSERT(min_tbl->contains_fact(row)); + + dealloc(min_fn); + min_tbl->deallocate(); + tbl->deallocate(); +} void tst_dl_table() { test_dl_bitvector_table(); + test_table_min(); } #else void tst_dl_table() { diff --git a/src/test/dl_util.cpp b/src/test/dl_util.cpp index 601fd630a..02e40bbd9 100644 --- a/src/test/dl_util.cpp +++ b/src/test/dl_util.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "dl_util.h" using namespace datalog; diff --git a/src/test/doc.cpp b/src/test/doc.cpp new file mode 100644 index 000000000..b2863a629 --- /dev/null +++ b/src/test/doc.cpp @@ -0,0 +1,493 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "doc.h" +#include "trace.h" +#include "vector.h" +#include "ast.h" +#include "ast_pp.h" +#include "reg_decl_plugins.h" +#include "sorting_network.h" +#include "smt_kernel.h" +#include "model_smt2_pp.h" +#include "smt_params.h" +#include "ast_util.h" +#include "expr_safe_replace.h" +#include "th_rewriter.h" + + +static void tst_doc1(unsigned n) { + doc_manager m(n); + + doc_ref d(m, m.allocate()); + doc_ref d1(m, m.allocate1()); + doc_ref d0(m, m.allocate0()); + doc_ref dX(m, m.allocateX()); + doc_ref dXc(m, m.allocate(*dX)); + doc_ref d10(m, m.allocate(10)); + doc_ref d20(m, m.allocate(rational(20))); + m.display(std::cout, *d1) << "\n"; + m.display(std::cout, *d0) << "\n"; + m.display(std::cout, *dX) << "\n"; + m.display(std::cout, *d10) << "\n"; + m.display(std::cout, *d20) << "\n"; + if (n < 64) { + unsigned hi = 3, lo = 1; + SASSERT(hi <= n); + doc_ref d111X(m, m.allocate(0xFF, hi, lo)); + } + m.copy(*d, *d10); + SASSERT(m.equals(*d, *d10)); + m.reset(*d); + SASSERT(!m.equals(*d, *d10)); + m.fill0(*d10); + SASSERT(m.equals(*d, *d10)); + m.fill1(*d); + d10 = m.allocate(10); + SASSERT(!m.equals(*d, *d10)); + SASSERT(m.equals(*d, *d1)); + m.fillX(*d); + SASSERT(m.equals(*d, *dX)); + SASSERT(m.is_full(*dX)); + SASSERT(!m.is_full(*d1)); + + VERIFY(m.set_and(*dX,*dX)); + SASSERT(m.equals(*dXc,*dX)); + VERIFY(m.set_and(*dX,*d1)); + SASSERT(!m.equals(*dXc,*dX)); + SASSERT(m.equals(*dX,*d1)); + VERIFY(m.fold_neg(*dX)); + ptr_vector result; + // VERIFY(!m.intersect(*d1,*d0, result)); + // m.subtract(*d1,*d0, result); + SASSERT(result.empty()); + dX = m.allocateX(); + m.display(std::cout, *d0) << "\n"; + m.display(std::cout, *dX) << "\n"; + SASSERT(m.contains(*dX,*d1)); + SASSERT(m.contains(*dX,*d0)); + SASSERT(!m.contains(*d0,*d1)); + SASSERT(!m.contains(*d1,*d0)); + + + d1->neg().push_back(m.tbvm().allocate0()); + m.display(std::cout, *d1) << " -> "; + VERIFY(m.fold_neg(*d1)); + m.display(std::cout, *d1) << "\n"; + + + bit_vector to_delete; + to_delete.resize(n, false); + to_delete.set(1); + to_delete.set(3); + doc_manager m1(n-2); + doc_ref d1_1(m1, m.project(m1, to_delete, *d1)); + doc_ref d1_2(m1, m1.allocate1()); + m.display(std::cout, *d1) << " -> "; + m1.display(std::cout, *d1_1) << "\n"; + SASSERT(m1.equals(*d1_1,*d1_2)); + m.set(*d1,2,BIT_x); + m.set(*d1,4,BIT_x); + d1_1 = m.project(m1, to_delete, *d1); + m.display(std::cout, *d1) << " -> "; + m1.display(std::cout, *d1_1) << "\n"; + d1->neg().push_back(m.tbvm().allocate1()); + SASSERT(m.well_formed(*d1)); + d1_1 = m.project(m1, to_delete, *d1); + m.display(std::cout, *d1) << " -> "; + m1.display(std::cout, *d1_1) << "\n"; +} + + +// generate "all" clauses over num_vars +// create XXXX \ clauses +// project 0, 1, 2, 3 variables +// check that result is the same as QE over those clauses. + +class test_doc_cls { + random_gen m_ran; + ast_manager m; + doc_manager dm; + expr_ref_vector m_vars; + + tbit choose_tbit() { + switch (m_ran(3)) { + case 0: return BIT_0; + case 1: return BIT_1; + default : return BIT_x; + } + } + + tbv* mk_rand_tbv() { + tbv* result = dm.tbvm().allocate(); + for (unsigned i = 0; i < dm.num_tbits(); ++i) { + dm.tbvm().set(*result, i, choose_tbit()); + } + return result; + } + + tbv* mk_rand_tbv(tbv const& pos) { + tbv* result = dm.tbvm().allocate(); + for (unsigned i = 0; i < dm.num_tbits(); ++i) { + if (pos[i] == BIT_x) { + dm.tbvm().set(*result, i, choose_tbit()); + } + else { + dm.tbvm().set(*result, i, pos[i]); + } + } + return result; + } + + doc* mk_rand_doc(unsigned num_diff) { + tbv_ref t(dm.tbvm()); + t = mk_rand_tbv(); + doc* result = dm.allocate(*t); + SASSERT(dm.tbvm().equals(*t, result->pos())); + for (unsigned i = 0; i < num_diff; ++i) { + result->neg().push_back(mk_rand_tbv(result->pos())); + } + SASSERT(dm.well_formed(*result)); + return result; + } + + void mk_rand_udoc(unsigned num_elems, unsigned num_diff, udoc& result) { + result.reset(dm); + for (unsigned i = 0; i < num_elems; ++i) { + result.push_back(mk_rand_doc(num_diff)); + } + } + + expr_ref mk_conj(tbv& t) { + expr_ref result(m); + expr_ref_vector conjs(m); + for (unsigned i = 0; i < m_vars.size(); ++i) { + tbit b = choose_tbit(); + dm.tbvm().set(t, i, b); + switch (b) { + case BIT_1: conjs.push_back(m_vars[i].get()); break; + case BIT_0: conjs.push_back(m.mk_not(m_vars[i].get())); break; + default: break; + } + } + result = mk_and(m, conjs.size(), conjs.c_ptr()); + return result; + } + + expr_ref to_formula(tbv const& t, doc_manager& m2) { + expr_ref result(m); + expr_ref_vector conjs(m); + unsigned n = m2.num_tbits(); + tbv_manager& tm = m2.tbvm(); + SASSERT(n <= m_vars.size()); + for (unsigned i = 0; i < n; ++i) { + switch (t[i]) { + case BIT_x: + break; + case BIT_1: + conjs.push_back(m_vars[i].get()); + break; + case BIT_0: + conjs.push_back(m.mk_not(m_vars[i].get())); + break; + default: + UNREACHABLE(); + break; + } + } + result = mk_and(m, conjs.size(), conjs.c_ptr()); + return result; + } + + expr_ref to_formula(doc const& d, doc_manager& m2) { + expr_ref result(m); + expr_ref_vector conjs(m); + conjs.push_back(to_formula(d.pos(), m2)); + for (unsigned i = 0; i < d.neg().size(); ++i) { + conjs.push_back(m.mk_not(to_formula(d.neg()[i], m2))); + } + result = mk_and(m, conjs.size(), conjs.c_ptr()); + return result; + } + + expr_ref to_formula(udoc const& ud, doc_manager& m2) { + expr_ref result(m); + expr_ref_vector disjs(m); + for (unsigned i = 0; i < ud.size(); ++i) { + disjs.push_back(to_formula(ud[i], m2)); + } + result = mk_or(m, disjs.size(), disjs.c_ptr()); + return result; + } + + void project(doc const& d, doc_manager& m2, const bit_vector& to_delete, doc_ref& result) { + result = dm.project(m2, to_delete, d); + TRACE("doc", + for (unsigned i = 0; i < m_vars.size(); ++i) { + tout << (to_delete.get(i)?"0":"1"); + } + tout << " "; + dm.display(tout, d) << " -> "; + m2.display(tout, *result) << "\n"; + ); + } + + + void test_project(unsigned num_clauses) { + doc_ref d(dm); + d = mk_rand_doc(3); + expr_ref fml1(m), fml2(m), fml3(m), tmp1(m), tmp2(m), fml(m); + fml1 = to_formula(*d, dm); + bit_vector to_delete; + to_delete.reserve(m_vars.size(), false); + unsigned num_bits = 1; + for (unsigned i = 1; i < to_delete.size(); ++i) { + to_delete.set(i, m_ran(2) == 0); + if (!to_delete.get(i)) ++num_bits; + } + doc_manager m2(num_bits); + doc_ref result(m2); + project(*d, m2, to_delete, result); + TRACE("doc", + dm.display(tout, *d) << "\n"; + m2.display(tout, *result) << "\n";); + fml2 = to_formula(*result, m2); + project_expand(fml1, to_delete); + project_rename(fml2, to_delete); + check_equiv(fml1, fml2); + } + + void project_expand(expr_ref& fml, bit_vector const& to_delete) { + expr_ref tmp1(m), tmp2(m); + for (unsigned i = 0; i < m_vars.size(); ++i) { + if (to_delete.get(i)) { + expr_safe_replace rep1(m), rep2(m); + rep1.insert(m_vars[i].get(), m.mk_true()); + rep1(fml, tmp1); + rep2.insert(m_vars[i].get(), m.mk_false()); + rep2(fml, tmp2); + if (tmp1 == tmp2) { + fml = tmp1; + } + else { + fml = m.mk_or(tmp1, tmp2); + } + } + } + } + + void project_rename(expr_ref& fml, bit_vector const& to_delete) { + expr_safe_replace rep(m); + for (unsigned i = 0, j = 0; i < m_vars.size(); ++i) { + if (!to_delete.get(i)) { + rep.insert(m_vars[j].get(), m_vars[i].get()); + ++j; + } + } + rep(fml); + } + + void test_merge(unsigned num_clauses) { + doc_ref d(dm, dm.allocateX()); + expr_ref_vector fmls(m), eqs(m); + unsigned N = m_vars.size(); + expr_ref fml1(m), fml2(m), fml3(m), tmp1(m), tmp2(m), fml(m); + for (unsigned i = 0; i < num_clauses; ++i) { + tbv* t = dm.tbvm().allocate(); + fmls.push_back(m.mk_not(mk_conj(*t))); + d->neg().push_back(t); + } + fml1 = mk_and(m, fmls.size(), fmls.c_ptr()); + svector to_merge(N, false); + bit_vector discard_cols; + discard_cols.resize(N, false); + unsigned num_bits = 1; + union_find_default_ctx union_ctx; + subset_ints equalities(union_ctx); + unsigned lo = N; + equalities.mk_var(); + for (unsigned i = 1; i < N; ++i) { + to_merge[i] = (m_ran(2) == 0); + if (!to_merge[i]) ++num_bits; else lo = i; + equalities.mk_var(); + } + if (lo == N) return; + for (unsigned i = 0; i < N; ++i) { + if (to_merge[i] && i != lo) { + equalities.merge(i, lo); + eqs.push_back(m.mk_eq(m_vars[i].get(), m_vars[lo].get())); + } + } + eqs.push_back(to_formula(*d, dm)); + fml1 = mk_and(m, eqs.size(), eqs.c_ptr()); + if (dm.merge(*d, lo, 1, equalities, discard_cols)) { + fml2 = to_formula(*d, dm); + } + else { + fml2 = m.mk_false(); + } + check_equiv(fml1, fml2); + } + + void check_equiv(expr_ref& fml1, expr_ref& fml2) { + th_rewriter rw(m); + rw(fml1); + rw(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"; + ); + } + SASSERT(res == l_false); + } + + +public: + test_doc_cls(unsigned num_vars): dm(num_vars), m_vars(m) { + reg_decl_plugins(m); + for (unsigned i = 0; i < num_vars; ++i) { + m_vars.push_back(m.mk_fresh_const("b", m.mk_bool_sort())); + } + } + + void test_project(unsigned num_rounds, unsigned num_clauses) { + for (unsigned i = 0; i < num_rounds; ++i) { + test_project(num_clauses); + } + } + + void test_merge(unsigned num_rounds, unsigned num_clauses) { + for (unsigned i = 0; i < num_rounds; ++i) { + test_merge(num_clauses); + } + } + + void test_project1() { + expr_ref fml1(m), fml2(m); + doc_ref d(dm, dm.allocateX()); + tbv_ref t(dm.tbvm(), dm.tbvm().allocateX()); + dm.tbvm().set(*t, 0, BIT_0); + d->neg().push_back(t.detach()); + unsigned num_bits = dm.num_tbits(); + bit_vector to_delete; + to_delete.reserve(num_bits, false); + fml1 = to_formula(*d, dm); + to_delete.set(0, true); + doc_manager m2(num_bits-1); + doc_ref result(m2); + project(*d, m2, to_delete, result); + dm.display(std::cout, *d) << "\n"; + m2.display(std::cout, *result) << "\n"; + fml2 = to_formula(*result, m2); + project_rename(fml2, to_delete); + project_expand(fml1, to_delete); + std::cout << fml1 << " " << fml2 << "\n"; + check_equiv(fml1, fml2); + } + + + void test_subtract() { + doc_ref d1(dm); + doc_ref d2(dm); + doc_ref d3(dm); + udoc ds1, ds2; + d1 = dm.allocateX(); + d2 = dm.allocateX(); + d3 = dm.allocateX(); + dm.set(*d1, 0, BIT_1); + dm.set(*d1, 1, BIT_0); + dm.set(*d2, 0, BIT_0); + dm.set(*d2, 1, BIT_1); + //ds1.push_back(d1.detach()); + ds1.push_back(d2.detach()); + // ds1 = {10x, 01x} + d1 = dm.allocateX(); + tbv_ref t1(dm.tbvm()); + tbv_ref t2(dm.tbvm()); + t1 = dm.tbvm().allocateX(); + t2 = dm.tbvm().allocateX(); + dm.tbvm().set(*t1, 0, BIT_1); + dm.tbvm().set(*t1, 2, BIT_0); + dm.tbvm().set(*t2, 0, BIT_0); + dm.tbvm().set(*t2, 2, BIT_1); + d1->neg().push_back(t1.detach()); + d1->neg().push_back(t2.detach()); + ds2.push_back(d1.detach()); + ds1.display(dm, std::cout) << "\n"; + ds2.display(dm, std::cout) << "\n"; + expr_ref fml1 = to_formula(ds1, dm); + expr_ref fml2 = to_formula(ds2, dm); + ds1.subtract(dm, ds2); + ds1.display(dm, std::cout) << "\n"; + expr_ref fml3 = to_formula(ds1, dm); + fml1 = m.mk_and(fml1, m.mk_not(fml2)); + check_equiv(fml1, fml3); + ds1.reset(dm); + ds2.reset(dm); + //sub:{xxx \ {1x0, 0x1}} + //result:{100} + + for (unsigned i = 0; i < 1000; ++i) { + udoc d1, d2; + mk_rand_udoc(3, 3, d1); + mk_rand_udoc(3, 3, d2); + fml1 = to_formula(d1, dm); + fml2 = to_formula(d2, dm); + d1.subtract(dm, d2); + fml3 = to_formula(d1, dm); + fml1 = m.mk_and(fml1, m.mk_not(fml2)); + check_equiv(fml1, fml3); + d1.reset(dm); + d2.reset(dm); + } + } + + void test_intersect() { + expr_ref fml1(m), fml2(m), fml3(m); + for (unsigned i = 0; i < 10000; ++i) { + udoc d1, d2; + mk_rand_udoc(3, 3, d1); + mk_rand_udoc(3, 3, d2); + fml1 = to_formula(d1, dm); + fml2 = to_formula(d2, dm); + TRACE("doc", + d1.display(dm, tout) << "\n"; + d2.display(dm, tout) << "\n";); + d1.intersect(dm, d2); + TRACE("doc", d1.display(dm, tout) << "\n";); + SASSERT(d1.well_formed(dm)); + fml3 = to_formula(d1, dm); + fml1 = m.mk_and(fml1, fml2); + check_equiv(fml1, fml3); + d1.reset(dm); + d2.reset(dm); + } + } + +}; + + +void tst_doc() { + + test_doc_cls tp(4); + tp.test_project1(); + tp.test_project(200,7); + + tp.test_intersect(); + tp.test_subtract(); + tp.test_merge(200,7); + + tst_doc1(5); + tst_doc1(10); + tst_doc1(70); +} diff --git a/src/test/expr_rand.cpp b/src/test/expr_rand.cpp index 19a07e98a..972c6e242 100644 --- a/src/test/expr_rand.cpp +++ b/src/test/expr_rand.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "expr_rand.h" #include "ast_pp.h" #include "bv_decl_plugin.h" diff --git a/src/test/expr_substitution.cpp b/src/test/expr_substitution.cpp index f83bde97d..8af3f7197 100644 --- a/src/test/expr_substitution.cpp +++ b/src/test/expr_substitution.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "expr_substitution.h" #include "smt_params.h" #include "substitution.h" diff --git a/src/test/factor_rewriter.cpp b/src/test/factor_rewriter.cpp index 625710fe1..3bcdcd9e9 100644 --- a/src/test/factor_rewriter.cpp +++ b/src/test/factor_rewriter.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "factor_rewriter.h" #include "bv_decl_plugin.h" #include "ast_pp.h" diff --git a/src/test/fixed_bit_vector.cpp b/src/test/fixed_bit_vector.cpp new file mode 100644 index 000000000..7bd9e62c3 --- /dev/null +++ b/src/test/fixed_bit_vector.cpp @@ -0,0 +1,118 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + fixed_bit_vector.cpp + +Abstract: + + Test fixed-size bit vector module + +Author: + + Nikolaj Bjorner (nbjorner) 2014-9-15. + +Revision History: + + based on bit_vector.cpp + +--*/ +#include +#include +#include"fixed_bit_vector.h" +#include"vector.h" + + +static void tst1() { + fixed_bit_vector_manager m(30); + fixed_bit_vector *b; + b = m.allocate0(); + m.set(*b, 0, true); + m.set(*b, 1, false); + m.set(*b, 2, true); + SASSERT(b->get(0) == true); + SASSERT(b->get(1) == false); + SASSERT(b->get(2) == true); + SASSERT(b->get(3) == false); + SASSERT(b->get(29) == false); + m.deallocate(b); +} + +static void tst_or() { + { + fixed_bit_vector_manager m(10); + fixed_bit_vector *b1, *b2; + b1 = m.allocate0(); + b2 = m.allocate0(); + + m.set(*b1, 4); + m.set(*b2, 8); + m.set(*b2, 3); + m.set(*b2, 2); + m.set(*b2, 1); + m.display(std::cout, *b1) << "\n"; + m.display(std::cout, *b2) << "\n"; + m.set_or(*b1, *b2); + m.display(std::cout, *b1) << "\n"; + SASSERT(!m.equals(*b1, *b2)); + m.unset(*b1, 4); + SASSERT(m.equals(*b1, *b2)); + m.unset(*b1, 3); + SASSERT(!m.equals(*b1, *b2)); + m.deallocate(b1); + m.deallocate(b2); + } +} + +static void tst_and() { + +} + + + +static void tst_eq(unsigned num_bits) { + fixed_bit_vector_manager m(num_bits); + fixed_bit_vector* b1 = m.allocate0(); + fixed_bit_vector* b2 = m.allocate0(); + fixed_bit_vector* b3 = m.allocate0(); + + m.set(*b1, 3, true); + SASSERT(!m.equals(*b1, *b2)); + SASSERT(m.equals(*b2, *b3)); + + m.set(*b3, 3, true); + SASSERT(m.equals(*b1, *b3)); + + m.set(*b2, num_bits-1, true); + m.set(*b3, num_bits-1); + m.unset(*b3, 3); + SASSERT(m.equals(*b2, *b3)); + m.fill0(*b1); + m.set_neg(*b1); + m.fill1(*b2); + SASSERT(m.equals(*b1, *b2)); + m.fill0(*b1); + for (unsigned i = 0; i < num_bits; ++i) { + m.set(*b1, i, true); + } + SASSERT(m.equals(*b1, *b2)); + m.deallocate(b1); + m.deallocate(b2); + m.deallocate(b3); +} + +void tst_fixed_bit_vector() { + tst1(); + tst_or(); + tst_and(); + tst_eq(15); + tst_eq(16); + tst_eq(17); + tst_eq(31); + tst_eq(32); + tst_eq(33); + tst_eq(63); + tst_eq(64); + tst_eq(65); +} diff --git a/src/test/fuzzing/expr_delta.cpp b/src/test/fuzzing/expr_delta.cpp index 344e555c9..fd5117a74 100644 --- a/src/test/fuzzing/expr_delta.cpp +++ b/src/test/fuzzing/expr_delta.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "expr_delta.h" #include "ast_pp.h" diff --git a/src/test/fuzzing/expr_rand.cpp b/src/test/fuzzing/expr_rand.cpp index a2aca7e6e..f56704e45 100644 --- a/src/test/fuzzing/expr_rand.cpp +++ b/src/test/fuzzing/expr_rand.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "expr_rand.h" #include "bv_decl_plugin.h" #include "array_decl_plugin.h" diff --git a/src/test/get_implied_equalities.cpp b/src/test/get_implied_equalities.cpp index 2576920cc..37fbe2004 100644 --- a/src/test/get_implied_equalities.cpp +++ b/src/test/get_implied_equalities.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "z3.h" #include "trace.h" #include "debug.h" diff --git a/src/test/heap_trie.cpp b/src/test/heap_trie.cpp index 92ef97f72..5b0047e82 100644 --- a/src/test/heap_trie.cpp +++ b/src/test/heap_trie.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "heap_trie.h" struct unsigned_le { diff --git a/src/test/hilbert_basis.cpp b/src/test/hilbert_basis.cpp index 4752dd78d..57c8c5050 100644 --- a/src/test/hilbert_basis.cpp +++ b/src/test/hilbert_basis.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "hilbert_basis.h" #include "ast_pp.h" #include "reg_decl_plugins.h" @@ -540,6 +546,33 @@ static void tst19() { saturate_basis(hb); } +static void test_A_5_5_3() { + hilbert_basis hb; + for (unsigned i = 0; i < 15; ++i) { + vector v; + for (unsigned j = 0; j < 5; ++j) { + for (unsigned k = 0; k < 15; ++k) { + v.push_back(rational(k == i)); + } + } + hb.add_ge(v, R(0)); + } + for (unsigned i = 1; i <= 15; ++i) { + vector v; + for (unsigned k = 1; k <= 5; ++k) { + for (unsigned l = 1; l <= 5; ++l) { + for (unsigned j = 1; j <= 3; ++j) { + bool one = ((j*k <= i) && (((i - j) % 3) == 0)); // fixme + v.push_back(rational(one)); + } + } + } + hb.add_ge(v, R(0)); + } + // etc. + saturate_basis(hb); +} + void tst_hilbert_basis() { std::cout << "hilbert basis test\n"; // tst3(); @@ -547,6 +580,9 @@ void tst_hilbert_basis() { g_use_ordered_support = true; + test_A_5_5_3(); + return; + tst18(); return; diff --git a/src/test/horn_subsume_model_converter.cpp b/src/test/horn_subsume_model_converter.cpp index 28359b954..4b653e3a8 100644 --- a/src/test/horn_subsume_model_converter.cpp +++ b/src/test/horn_subsume_model_converter.cpp @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #include "horn_subsume_model_converter.h" #include "arith_decl_plugin.h" #include "model_smt2_pp.h" diff --git a/src/test/karr.cpp b/src/test/karr.cpp index 87debf662..4c0737230 100644 --- a/src/test/karr.cpp +++ b/src/test/karr.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "hilbert_basis.h" /* diff --git a/src/test/main.cpp b/src/test/main.cpp index bc7e04124..be12286fb 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -140,6 +140,10 @@ int main(int argc, char ** argv) { TST(ast); TST(optional); TST(bit_vector); + TST(fixed_bit_vector); + TST(tbv); + TST(doc); + TST(udoc_relation); TST(string_buffer); TST(map); TST(diff_logic); @@ -216,6 +220,13 @@ int main(int argc, char ** argv) { TST(polynorm); TST(qe_arith); TST(expr_substitution); + TST(sorting_network); + TST(theory_pb); + TST(simplex); + TST(sat_user_scope); + TST(pdr); + TST_ARGV(ddnf); + //TST_ARGV(hs); } void initialize_mam() {} diff --git a/src/test/memory.cpp b/src/test/memory.cpp index 0f5a92e2c..340a1929c 100644 --- a/src/test/memory.cpp +++ b/src/test/memory.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifdef _WINDOWS #include "z3.h" #include "z3_private.h" diff --git a/src/test/model2expr.cpp b/src/test/model2expr.cpp index abca706c1..f96cee55f 100644 --- a/src/test/model2expr.cpp +++ b/src/test/model2expr.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "model2expr.h" #include "ast_pp.h" #include "arith_decl_plugin.h" diff --git a/src/test/model_retrieval.cpp b/src/test/model_retrieval.cpp index f40f89d22..e42e22539 100644 --- a/src/test/model_retrieval.cpp +++ b/src/test/model_retrieval.cpp @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #include "ast.h" #include "smt_params.h" #include "smt_context.h" diff --git a/src/test/nlarith_util.cpp b/src/test/nlarith_util.cpp index f08fa020e..2def66b38 100644 --- a/src/test/nlarith_util.cpp +++ b/src/test/nlarith_util.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "nlarith_util.h" #include "arith_decl_plugin.h" #include "ast_pp.h" diff --git a/src/test/pdr.cpp b/src/test/pdr.cpp new file mode 100644 index 000000000..a1410b482 --- /dev/null +++ b/src/test/pdr.cpp @@ -0,0 +1,128 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "pdr_context.h" +#include "reg_decl_plugins.h" + + +using namespace pdr; + +static expr_ref mk_state(expr_ref_vector const& states, random_gen& rand) { + expr_ref result(states.get_manager()); + result = states[rand(states.size())]; + return result; +} + + +struct test_model_search { + struct init_test { + init_test(func_decl_ref& fn) { + ast_manager& m = fn.get_manager(); + reg_decl_plugins(m); + fn = m.mk_const_decl(symbol("f"), m.mk_bool_sort()); + } + }; + + ast_manager m; + smt_params smt_params; + fixedpoint_params fp_params; + context ctx; + manager pm; + func_decl_ref fn; + init_test initt; + pred_transformer pt; + random_gen rand; + model_search search; + expr_ref_vector states; + + + test_model_search(): + ctx(smt_params, fp_params, m), + pm(smt_params, 10, m), + fn(m), + initt(fn), + pt(ctx, pm, fn), + rand(10), + search(true), + states(m) { + } + + void add_tree(model_node* parent, bool force_goal) { + unsigned level = parent->level(); + search.add_leaf(*parent); + expr_ref state(m); + if (level > 0 && (force_goal || parent->is_goal())) { + search.remove_goal(*parent); + + state = mk_state(states, rand); + add_tree(alloc(model_node, parent, state, pt, level-1), false); + + state = mk_state(states, rand); + add_tree(alloc(model_node, parent, state, pt, level-1), false); + parent->check_pre_closed(); + } + } + + bool mutate() { + model_node* leaf = search.next(); + if (!leaf) return false; + unsigned level = leaf->level(); + if (level == 0) { + if (rand(2) == 0) { + leaf->display(std::cout << "backtrack to grandparent\n", 1); + search.backtrack_level(false, *leaf->parent()); + } + else { + leaf->display(std::cout << "backtrack to parent\n", 1); + search.backtrack_level(false, *leaf); + } + } + else { + leaf->display(std::cout << "grow tree\n", 1); + add_tree(leaf, true); + } + return true; + } + + void init() { + std::cout << "pdr state-hash search tree\n"; + + expr_ref state(m); + func_decl_ref fn(m); + for (unsigned i = 0; i < 10; ++i) { + std::ostringstream strm; + strm << "s" << i; + state = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); + fn = to_app(state)->get_decl(); + states.push_back(state); + } + state = states[0].get(); + unsigned level = 4; + for(unsigned n = 0; n < 100; ++n) { + state = mk_state(states, rand); + model_node* root = alloc(model_node, 0, state, pt, level); + search.set_root(root); + add_tree(root, false); + search.display(std::cout); + + while (true) { + search.well_formed(); + if (!mutate()) break; + search.display(std::cout); + } + search.reset(); + //++level; + } + search.reset(); + } + +}; + +void tst_pdr() { + test_model_search test; + + test.init(); +} diff --git a/src/test/polynomial_factorization.cpp b/src/test/polynomial_factorization.cpp index 06c67b9c2..7af0742ef 100644 --- a/src/test/polynomial_factorization.cpp +++ b/src/test/polynomial_factorization.cpp @@ -24,7 +24,8 @@ Notes: #include"polynomial_factorization.h" #endif -using namespace std; +using std::cout; +using std::endl; // some prime numbers unsigned primes[] = { diff --git a/src/test/polynorm.cpp b/src/test/polynorm.cpp index 3593e98ce..a8f4ab861 100644 --- a/src/test/polynorm.cpp +++ b/src/test/polynorm.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "th_rewriter.h" #include "smt2parser.h" #include "arith_decl_plugin.h" diff --git a/src/test/proof_checker.cpp b/src/test/proof_checker.cpp index 12a5245e6..7f9827187 100644 --- a/src/test/proof_checker.cpp +++ b/src/test/proof_checker.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "proof_checker.h" #include "ast_ll_pp.h" diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index 8b6577b3f..bd8266dc2 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "qe_arith.h" #include "qe.h" #include "th_rewriter.h" diff --git a/src/test/quant_elim.cpp b/src/test/quant_elim.cpp index 15d23a574..0fe7f8eec 100644 --- a/src/test/quant_elim.cpp +++ b/src/test/quant_elim.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "ast.h" #include "smt_params.h" #include "simplifier.h" diff --git a/src/test/quant_solve.cpp b/src/test/quant_solve.cpp index ae2dadee9..aa1306128 100644 --- a/src/test/quant_solve.cpp +++ b/src/test/quant_solve.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "ast.h" #include "smt_params.h" #include "qe.h" diff --git a/src/test/sat_user_scope.cpp b/src/test/sat_user_scope.cpp new file mode 100644 index 000000000..5107361bd --- /dev/null +++ b/src/test/sat_user_scope.cpp @@ -0,0 +1,108 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "sat_solver.h" +#include "util.h" + +typedef sat::literal_vector clause_t; +typedef vector clauses_t; +typedef vector trail_t; + +// [ [c1, c2, ..], [ ...] ] + +static unsigned s_num_vars = 6; +static unsigned s_num_clauses_per_frame = 8; +static unsigned s_num_frames = 7; + +static void add_literal(random_gen& r, clause_t& c) { + c.push_back(sat::literal(r(s_num_vars) + 1, r(2) == 0)); +} + +static clause_t& last_clause(trail_t& t) { + return t.back().back(); +} + +static void add_clause(sat::solver& s, random_gen& r, trail_t& t) { + t.back().push_back(sat::literal_vector()); + clause_t& cls = last_clause(t); + for (unsigned i = 0; i < 3; ++i) { + add_literal(r, cls); + } + s.mk_clause(cls.size(), cls.c_ptr()); +} + +static void display_state(std::ostream& out, sat::solver& s, trail_t& t) { + s.display(out); +} + +static void pop_user_scope(sat::solver& s, trail_t& t) { + std::cout << "pop\n"; + s.user_pop(1); + t.pop_back(); +} + +static void push_user_scope(sat::solver& s, trail_t& t) { + std::cout << "push\n"; + s.user_push(); + t.push_back(clauses_t()); +} + +static void init_vars(sat::solver& s) { + for (unsigned i = 0; i <= s_num_vars; ++i) { + s.mk_var(); + } +} + +static void check_coherence(sat::solver& s1, trail_t& t) { + params_ref p; + sat::solver s2(p, 0); + init_vars(s2); + sat::literal_vector cls; + for (unsigned i = 0; i < t.size(); ++i) { + clauses_t& clss = t[i]; + for (unsigned j = 0; j < clss.size(); ++j) { + cls.reset(); + cls.append(clss[j]); + s2.mk_clause(cls.size(), cls.c_ptr()); + } + } + lbool is_sat1 = s1.check(); + lbool is_sat2 = s2.check(); + if (is_sat1 != is_sat2) { + s1.display(std::cout); + s2.display(std::cout); + } + std::cout << is_sat1 << "\n"; + SASSERT(is_sat1 == is_sat2); +} + +void tst_sat_user_scope() { + random_gen r(0); + trail_t trail; + params_ref p; + sat::solver s(p, 0); // incremental solver + init_vars(s); + while (true) { + for (unsigned i = 0; i < s_num_frames; ++i) { + // push 3 frames, pop 2 + for (unsigned k = 0; k < 3; ++k) { + push_user_scope(s, trail); + for (unsigned j = 0; j < s_num_clauses_per_frame; ++j) { + add_clause(s, r, trail); + } + check_coherence(s, trail); + } + for (unsigned k = 0; k < 2; ++k) { + pop_user_scope(s, trail); + check_coherence(s, trail); + } + } + for (unsigned i = 0; i < s_num_frames; ++i) { + pop_user_scope(s, trail); + check_coherence(s, trail); + } + } +} diff --git a/src/test/simplex.cpp b/src/test/simplex.cpp new file mode 100644 index 000000000..61ab7180b --- /dev/null +++ b/src/test/simplex.cpp @@ -0,0 +1,168 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "sparse_matrix.h" +#include "sparse_matrix_def.h" +#include "simplex.h" +#include "simplex_def.h" +#include "mpq_inf.h" +#include "vector.h" +#include "rational.h" + +#define R rational +typedef simplex::simplex Simplex; +typedef simplex::sparse_matrix sparse_matrix; + +static vector vec(int i, int j) { + vector nv; + nv.resize(2); + nv[0] = R(i); + nv[1] = R(j); + return nv; +} + +static vector vec(int i, int j, int k) { + vector nv = vec(i, j); + nv.push_back(R(k)); + return nv; +} + +static vector vec(int i, int j, int k, int l) { + vector nv = vec(i, j, k); + nv.push_back(R(l)); + return nv; +} + +static vector vec(int i, int j, int k, int l, int x) { + vector nv = vec(i, j, k, l); + nv.push_back(R(x)); + return nv; +} + +static vector vec(int i, int j, int k, int l, int x, int y) { + vector nv = vec(i, j, k, l, x); + nv.push_back(R(y)); + return nv; +} + +static vector vec(int i, int j, int k, int l, int x, int y, int z) { + vector nv = vec(i, j, k, l, x, y); + nv.push_back(R(z)); + return nv; +} + + + +void add_row(Simplex& S, vector const& _v, R const& _b, bool is_eq = false) { + unsynch_mpz_manager m; + unsigned_vector vars; + vector v(_v); + R b(_b); + R l(denominator(b)); + scoped_mpz_vector coeffs(m); + for (unsigned i = 0; i < v.size(); ++i) { + l = lcm(l, denominator(v[i])); + vars.push_back(i); + S.ensure_var(i); + } + b *= l; + b.neg(); + for (unsigned i = 0; i < v.size(); ++i) { + v[i] *= l; + coeffs.push_back(v[i].to_mpq().numerator()); + } + unsigned nv = S.get_num_vars(); + vars.push_back(nv); + vars.push_back(nv+1); + S.ensure_var(nv); + S.ensure_var(nv+1); + coeffs.push_back(mpz(-1)); + coeffs.push_back(b.to_mpq().numerator()); + mpq_inf one(mpq(1),mpq(0)); + mpq_inf zero(mpq(0),mpq(0)); + SASSERT(vars.size() == coeffs.size()); + S.set_lower(nv, zero); + if (is_eq) S.set_upper(nv, zero); + S.set_lower(nv+1, one); + S.set_upper(nv+1, one); + S.add_row(nv, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); +} + +static void feas(Simplex& S) { + S.display(std::cout); + lbool is_sat = S.make_feasible(); + std::cout << "feasible: " << is_sat << "\n"; + S.display(std::cout); +} + +static void test1() { + Simplex S; + add_row(S, vec(1,0), R(1)); + add_row(S, vec(0,1), R(1)); + add_row(S, vec(1,1), R(1)); + feas(S); +} + +static void test2() { + Simplex S; + add_row(S, vec(1, 0), R(1)); + add_row(S, vec(0, 1), R(1)); + add_row(S, vec(1, 1), R(1), true); + feas(S); +} + +static void test3() { + Simplex S; + add_row(S, vec(-1, 0), R(-1)); + add_row(S, vec(0, -1), R(-1)); + add_row(S, vec(1, 1), R(1), true); + feas(S); +} + +static void test4() { + Simplex S; + add_row(S, vec(1, 0), R(1)); + add_row(S, vec(0, -1), R(-1)); + add_row(S, vec(1, 1), R(1), true); + feas(S); +} + +void tst_simplex() { + Simplex S; + + std::cout << "simplex\n"; + + lbool is_sat = S.make_feasible(); + std::cout << "feasible: " << is_sat << "\n"; + + unsynch_mpz_manager m; + unsynch_mpq_inf_manager em; + scoped_mpz_vector coeffs(m); + svector vars; + for (unsigned i = 0; i < 5; ++i) { + S.ensure_var(i); + vars.push_back(i); + coeffs.push_back(mpz(i+1)); + } + + Simplex::row r = S.add_row(1, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); + is_sat = S.make_feasible(); + std::cout << "feasible: " << is_sat << "\n"; + S.display(std::cout); + _scoped_numeral num(em); + num = std::make_pair(mpq(1), mpq(0)); + S.set_lower(0, num); + S.set_upper(0, num); + + is_sat = S.make_feasible(); + std::cout << "feasible: " << is_sat << "\n"; + S.display(std::cout); + + test1(); + test2(); + test3(); + test4(); +} diff --git a/src/test/simplifier.cpp b/src/test/simplifier.cpp index 733c9e23a..2e8f434e1 100644 --- a/src/test/simplifier.cpp +++ b/src/test/simplifier.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #ifdef _WINDOWS #include "z3.h" #include "z3_private.h" diff --git a/src/test/small_object_allocator.cpp b/src/test/small_object_allocator.cpp index 1e74a734b..1ecf865dd 100644 --- a/src/test/small_object_allocator.cpp +++ b/src/test/small_object_allocator.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include #include"util.h" #include"trace.h" diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 39543d141..982e2e3f5 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + // This is to test the print-parse facilities over the API // for SMT-LIB2. diff --git a/src/test/smt_context.cpp b/src/test/smt_context.cpp index 49ada8ebd..6c44b8b1d 100644 --- a/src/test/smt_context.cpp +++ b/src/test/smt_context.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "smt_context.h" #include "reg_decl_plugins.h" diff --git a/src/test/sorting_network.cpp b/src/test/sorting_network.cpp new file mode 100644 index 000000000..4802e2bec --- /dev/null +++ b/src/test/sorting_network.cpp @@ -0,0 +1,346 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "trace.h" +#include "vector.h" +#include "ast.h" +#include "ast_pp.h" +#include "reg_decl_plugins.h" +#include "sorting_network.h" +#include "smt_kernel.h" +#include "model_smt2_pp.h" +#include "smt_params.h" +#include "ast_util.h" + + + +struct ast_ext { + ast_manager& m; + ast_ext(ast_manager& m):m(m) {} + typedef expr* T; + typedef expr_ref_vector vector; + T mk_ite(T a, T b, T c) { + return m.mk_ite(a, b, c); + } + T mk_le(T a, T b) { + if (m.is_bool(a)) { + return m.mk_implies(a, b); + } + UNREACHABLE(); + return 0; + } + T mk_default() { + return m.mk_false(); + } +}; + + + +struct unsigned_ext { + unsigned_ext() {} + typedef unsigned T; + typedef svector vector; + T mk_ite(T a, T b, T c) { + return (a==1)?b:c; + } + T mk_le(T a, T b) { + return (a <= b)?1:0; + } + T mk_default() { + return 0; + } +}; + + +static void is_sorted(svector const& v) { + for (unsigned i = 0; i + 1 < v.size(); ++i) { + SASSERT(v[i] <= v[i+1]); + } +} + +static void test_sorting1() { + svector in, out; + unsigned_ext uext; + sorting_network sn(uext); + + in.push_back(0); + in.push_back(1); + in.push_back(0); + in.push_back(1); + in.push_back(1); + in.push_back(0); + + sn(in, out); + + is_sorted(out); + for (unsigned i = 0; i < out.size(); ++i) { + std::cout << out[i]; + } + std::cout << "\n"; +} + +static void test_sorting2() { + svector in, out; + unsigned_ext uext; + sorting_network sn(uext); + + in.push_back(0); + in.push_back(1); + in.push_back(2); + in.push_back(1); + in.push_back(1); + in.push_back(3); + + sn(in, out); + + is_sorted(out); + + for (unsigned i = 0; i < out.size(); ++i) { + std::cout << out[i]; + } + std::cout << "\n"; +} + +static void test_sorting4_r(unsigned i, svector& in) { + if (i == in.size()) { + svector out; + unsigned_ext uext; + sorting_network sn(uext); + sn(in, out); + is_sorted(out); + std::cout << "sorted\n"; + } + else { + in[i] = 0; + test_sorting4_r(i+1, in); + in[i] = 1; + test_sorting4_r(i+1, in); + } +} + +static void test_sorting4() { + svector in; + in.resize(5); + test_sorting4_r(0, in); + in.resize(8); + test_sorting4_r(0, in); +} + +void test_sorting3() { + ast_manager m; + reg_decl_plugins(m); + expr_ref_vector in(m), out(m); + for (unsigned i = 0; i < 7; ++i) { + in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); + } + for (unsigned i = 0; i < in.size(); ++i) { + std::cout << mk_pp(in[i].get(), m) << "\n"; + } + ast_ext aext(m); + sorting_network sn(aext); + sn(in, out); + std::cout << "size: " << out.size() << "\n"; + for (unsigned i = 0; i < out.size(); ++i) { + std::cout << mk_pp(out[i].get(), m) << "\n"; + } +} + + +struct ast_ext2 { + ast_manager& m; + expr_ref_vector m_clauses; + expr_ref_vector m_trail; + ast_ext2(ast_manager& m):m(m), m_clauses(m), m_trail(m) {} + typedef expr* literal; + typedef ptr_vector literal_vector; + + expr* trail(expr* e) { + m_trail.push_back(e); + return e; + } + + literal mk_false() { return m.mk_false(); } + literal mk_true() { return m.mk_true(); } + literal mk_max(literal a, literal b) { + return trail(m.mk_or(a, b)); + } + literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } + literal mk_not(literal a) { if (m.is_not(a,a)) return a; + return trail(m.mk_not(a)); + } + std::ostream& pp(std::ostream& out, literal lit) { + return out << mk_pp(lit, m); + } + literal fresh() { + return trail(m.mk_fresh_const("x", m.mk_bool_sort())); + } + void mk_clause(unsigned n, literal const* lits) { + m_clauses.push_back(mk_or(m, n, lits)); + } +}; + + +static void test_sorting_eq(unsigned n, unsigned k) { + SASSERT(k < n); + ast_manager m; + reg_decl_plugins(m); + ast_ext2 ext(m); + expr_ref_vector in(m), out(m); + for (unsigned i = 0; i < n; ++i) { + in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); + } + smt_params fp; + smt::kernel solver(m, fp); + psort_nw sn(ext); + expr_ref result(m); + + // equality: + std::cout << "eq " << k << "\n"; + solver.push(); + result = sn.eq(k, in.size(), in.c_ptr()); + solver.assert_expr(result); + for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { + solver.assert_expr(ext.m_clauses[i].get()); + } + lbool res = solver.check(); + SASSERT(res == l_true); + + solver.push(); + for (unsigned i = 0; i < k; ++i) { + solver.assert_expr(in[i].get()); + } + res = solver.check(); + SASSERT(res == l_true); + solver.assert_expr(in[k].get()); + res = solver.check(); + if (res == l_true) { + TRACE("pb", + unsigned sz = solver.size(); + for (unsigned i = 0; i < sz; ++i) { + tout << mk_pp(solver.get_formulas()[i], m) << "\n"; + }); + model_ref model; + solver.get_model(model); + model_smt2_pp(std::cout, m, *model, 0); + TRACE("pb", model_smt2_pp(tout, m, *model, 0);); + } + SASSERT(res == l_false); + solver.pop(1); + ext.m_clauses.reset(); +} + +static void test_sorting_le(unsigned n, unsigned k) { + ast_manager m; + reg_decl_plugins(m); + ast_ext2 ext(m); + expr_ref_vector in(m), out(m); + for (unsigned i = 0; i < n; ++i) { + in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); + } + smt_params fp; + smt::kernel solver(m, fp); + psort_nw sn(ext); + expr_ref result(m); + // B <= k + std::cout << "le " << k << "\n"; + solver.push(); + result = sn.le(false, k, in.size(), in.c_ptr()); + solver.assert_expr(result); + for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { + solver.assert_expr(ext.m_clauses[i].get()); + } + lbool res = solver.check(); + SASSERT(res == l_true); + + for (unsigned i = 0; i < k; ++i) { + solver.assert_expr(in[i].get()); + } + res = solver.check(); + SASSERT(res == l_true); + solver.assert_expr(in[k].get()); + res = solver.check(); + if (res == l_true) { + TRACE("pb", + unsigned sz = solver.size(); + for (unsigned i = 0; i < sz; ++i) { + tout << mk_pp(solver.get_formulas()[i], m) << "\n"; + }); + model_ref model; + solver.get_model(model); + model_smt2_pp(std::cout, m, *model, 0); + TRACE("pb", model_smt2_pp(tout, m, *model, 0);); + } + SASSERT(res == l_false); + solver.pop(1); + ext.m_clauses.reset(); +} + + +void test_sorting_ge(unsigned n, unsigned k) { + ast_manager m; + reg_decl_plugins(m); + ast_ext2 ext(m); + expr_ref_vector in(m), out(m); + for (unsigned i = 0; i < n; ++i) { + in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); + } + smt_params fp; + smt::kernel solver(m, fp); + psort_nw sn(ext); + expr_ref result(m); + // k <= B + std::cout << "ge " << k << "\n"; + solver.push(); + result = sn.ge(false, k, in.size(), in.c_ptr()); + solver.assert_expr(result); + for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { + solver.assert_expr(ext.m_clauses[i].get()); + } + lbool res = solver.check(); + SASSERT(res == l_true); + + solver.push(); + for (unsigned i = 0; i < n - k; ++i) { + solver.assert_expr(m.mk_not(in[i].get())); + } + res = solver.check(); + SASSERT(res == l_true); + solver.assert_expr(m.mk_not(in[n - k].get())); + res = solver.check(); + if (res == l_true) { + TRACE("pb", + unsigned sz = solver.size(); + for (unsigned i = 0; i < sz; ++i) { + tout << mk_pp(solver.get_formulas()[i], m) << "\n"; + }); + model_ref model; + solver.get_model(model); + model_smt2_pp(std::cout, m, *model, 0); + TRACE("pb", model_smt2_pp(tout, m, *model, 0);); + } + SASSERT(res == l_false); + solver.pop(1); +} + +void test_sorting5(unsigned n, unsigned k) { + std::cout << "n: " << n << " k: " << k << "\n"; + test_sorting_le(n, k); + test_sorting_eq(n, k); + test_sorting_ge(n, k); +} + +void tst_sorting_network() { + test_sorting_eq(11,7); + for (unsigned n = 3; n < 20; n += 2) { + for (unsigned k = 1; k < n; ++k) { + test_sorting5(n, k); + } + } + test_sorting1(); + test_sorting2(); + test_sorting3(); + test_sorting4(); +} diff --git a/src/test/substitution.cpp b/src/test/substitution.cpp index 0d7fb5856..cdc7f2080 100644 --- a/src/test/substitution.cpp +++ b/src/test/substitution.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "expr_substitution.h" #include "smt_params.h" #include "substitution.h" diff --git a/src/test/tbv.cpp b/src/test/tbv.cpp new file mode 100644 index 000000000..7637c7e83 --- /dev/null +++ b/src/test/tbv.cpp @@ -0,0 +1,91 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "tbv.h" + +static void tst1(unsigned num_bits) { + tbv_manager m(num_bits); + + tbv* b1 = m.allocate1(); + tbv* b0 = m.allocate0(); + tbv* bX = m.allocateX(); + tbv* bN = m.allocate(31); + m.display(std::cout, *b0) << "\n"; + m.display(std::cout, *b1) << "\n"; + m.display(std::cout, *bX) << "\n"; + m.display(std::cout, *bN) << "\n"; + SASSERT(!m.equals(*b1,*b0)); + SASSERT(!m.equals(*b1,*bX)); + SASSERT(!m.equals(*b0,*bX)); + m.set_and(*bX,*b0); + SASSERT(m.equals(*b0,*bX)); + SASSERT(!m.equals(*b1,*bX)); + m.copy(*bX,*b1); + SASSERT(m.equals(*b1,*bX)); + SASSERT(!m.equals(*b0,*bX)); + m.fillX(*bX); + VERIFY(m.intersect(*bX,*b0,*bN)); + SASSERT(m.equals(*b0, *bN)); + VERIFY(!m.intersect(*b0,*b1,*bN)); + m.fill1(*b1); + bit_vector to_delete; + to_delete.reserve(num_bits, false); + tbv_manager m2(num_bits-2); + to_delete.set(1); + to_delete.set(3); + m.set(*b1, 2, BIT_0); + m.set(*b1, 4, BIT_x); + tbv_ref b2(m2, m2.project(to_delete, *b1)); + m.display(std::cout, *b1) << " -> "; + m2.display(std::cout, *b2) << "\n"; + m.deallocate(b0); + m.deallocate(b1); + m.deallocate(bX); + m.deallocate(bN); + +} + +static void tst0() { + tbv_manager m(0); + + tbv_ref t1(m), t2(m), t3(m); + t1 = m.allocate1(); + t2 = m.allocate0(); + t3 = m.allocateX(); + m.display(std::cout, *t1) << "\n"; + m.display(std::cout, *t2) << "\n"; + m.display(std::cout, *t3) << "\n"; + SASSERT(m.equals(*t1, *t2)); + SASSERT(m.equals(*t1, *t3)); +} + +static void tst2(unsigned num_bits) { + tbv_manager m(num_bits); + tbv_ref t(m), t2(m); + for (unsigned i = 0; i < 55; ++i) { + t = m.allocate(i); + SASSERT(m.is_well_formed(*t)); + t2 = m.allocate(i+1); + VERIFY(!m.set_and(*t2, *t)); + SASSERT(!m.is_well_formed(*t2)); + } +} + +void tst_tbv() { + tst0(); + + tst1(31); + tst1(11); + tst1(15); + tst1(16); + tst1(17); + + tst2(31); + tst2(11); + tst2(15); + tst2(16); + tst2(17); +} diff --git a/src/test/test_util.h b/src/test/test_util.h index c83a94dbf..c81524b0e 100644 --- a/src/test/test_util.h +++ b/src/test/test_util.h @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #pragma once #include "stopwatch.h" diff --git a/src/test/theory_dl.cpp b/src/test/theory_dl.cpp index 9521a8932..463625c88 100644 --- a/src/test/theory_dl.cpp +++ b/src/test/theory_dl.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include "smt_context.h" #include "dl_decl_plugin.h" #include "ast_pp.h" diff --git a/src/test/theory_pb.cpp b/src/test/theory_pb.cpp new file mode 100644 index 000000000..3a229d951 --- /dev/null +++ b/src/test/theory_pb.cpp @@ -0,0 +1,170 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "smt_context.h" +#include "ast_pp.h" +#include "model_v2_pp.h" +#include "reg_decl_plugins.h" +#include "theory_pb.h" +#include "th_rewriter.h" + +unsigned populate_literals(unsigned k, smt::literal_vector& lits) { + SASSERT(k < (1u << lits.size())); + unsigned t = 0; + for (unsigned i = 0; i < lits.size(); ++i) { + if (k & (1 << i)) { + lits[i] = smt::true_literal; + t++; + } + else { + lits[i] = smt::false_literal; + } + } + return t; +} + +class pb_fuzzer { + ast_manager& m; + random_gen rand; + smt_params params; + smt::context ctx; + expr_ref_vector vars; + +public: + pb_fuzzer(ast_manager& m): m(m), rand(0), ctx(m, params), vars(m) { + params.m_model = true; + params.m_pb_enable_simplex = true; + unsigned N = 3; + for (unsigned i = 0; i < N; ++i) { + std::stringstream strm; + strm << "b" << i; + vars.push_back(m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort())); + std::cout << "(declare-const " << strm.str() << " Bool)\n"; + } + } + + void fuzz() { + enable_trace("pb"); + enable_trace("simplex"); + unsigned nr = 0; + for (unsigned i = 0; i < 100000; ++i) { + fuzz_round(nr, 2); + } + } + +private: + + void add_ineq() { + pb_util pb(m); + expr_ref fml(m), tmp(m); + th_rewriter rw(m); + vector coeffs(vars.size()); + expr_ref_vector args(vars); + while (true) { + rational k(rand(6)); + for (unsigned i = 0; i < coeffs.size(); ++i) { + int v = 3 - rand(5); + coeffs[i] = rational(v); + if (coeffs[i].is_neg()) { + args[i] = m.mk_not(args[i].get()); + coeffs[i].neg(); + k += coeffs[i]; + } + } + fml = pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), k); + rw(fml, tmp); + rw(tmp, tmp); + if (pb.is_ge(tmp)) { + fml = tmp; + break; + } + } + std::cout << "(assert " << fml << ")\n"; + ctx.assert_expr(fml); + } + + + + void fuzz_round(unsigned& num_rounds, unsigned lvl) { + unsigned num_rounds2 = 0; + lbool is_sat = l_true; + std::cout << "(push)\n"; + ctx.push(); + unsigned r = 0; + while (is_sat == l_true && r <= num_rounds + 1) { + add_ineq(); + std::cout << "(check-sat)\n"; + is_sat = ctx.check(); + if (lvl > 0 && is_sat == l_true) { + fuzz_round(num_rounds2, lvl-1); + } + ++r; + } + num_rounds = r; + std::cout << "; number of rounds: " << num_rounds << " level: " << lvl << "\n"; + ctx.pop(1); + std::cout << "(pop)\n"; + } + +}; + + + +static void fuzz_pb() +{ + ast_manager m; + reg_decl_plugins(m); + pb_fuzzer fuzzer(m); + fuzzer.fuzz(); +} + +void tst_theory_pb() { + + fuzz_pb(); + + ast_manager m; + smt_params params; + params.m_model = true; + reg_decl_plugins(m); + expr_ref tmp(m); + + enable_trace("pb"); + for (unsigned N = 4; N < 11; ++N) { + for (unsigned i = 0; i < (1u << N); ++i) { + smt::literal_vector lits(N, smt::false_literal); + unsigned k = populate_literals(i, lits); + std::cout << "k:" << k << " " << N << "\n"; + std::cout.flush(); + TRACE("pb", tout << "k " << k << ": "; + for (unsigned j = 0; j < lits.size(); ++j) { + tout << lits[j] << " "; + } + tout << "\n";); + { + smt::context ctx(m, params); + ctx.push(); + smt::literal l = smt::theory_pb::assert_ge(ctx, k+1, lits.size(), lits.c_ptr()); + if (l != smt::false_literal) { + ctx.assign(l, 0, false); + TRACE("pb", tout << "assign: " << l << "\n"; + ctx.display(tout);); + VERIFY(l_false == ctx.check()); + } + ctx.pop(1); + } + { + smt::context ctx(m, params); + ctx.push(); + smt::literal l = smt::theory_pb::assert_ge(ctx, k, lits.size(), lits.c_ptr()); + SASSERT(l != smt::false_literal); + ctx.assign(l, 0, false); + TRACE("pb", ctx.display(tout);); + VERIFY(l_true == ctx.check()); + ctx.pop(1); + } + } + } +} diff --git a/src/test/timeout.cpp b/src/test/timeout.cpp index 19daebb12..320006928 100644 --- a/src/test/timeout.cpp +++ b/src/test/timeout.cpp @@ -1,4 +1,10 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + + #include "timeout.h" #include "trace.h" diff --git a/src/test/udoc_relation.cpp b/src/test/udoc_relation.cpp new file mode 100644 index 000000000..0545ed62c --- /dev/null +++ b/src/test/udoc_relation.cpp @@ -0,0 +1,881 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "udoc_relation.h" +#include "trace.h" +#include "vector.h" +#include "ast.h" +#include "ast_pp.h" +#include "reg_decl_plugins.h" +#include "sorting_network.h" +#include "smt_kernel.h" +#include "model_smt2_pp.h" +#include "smt_params.h" +#include "ast_util.h" +#include "expr_safe_replace.h" +#include "th_rewriter.h" +#include "dl_relation_manager.h" +#include "dl_register_engine.h" +#include "rel_context.h" +#include "bv_decl_plugin.h" +#include "check_relation.h" + + +class udoc_tester { + typedef datalog::relation_base relation_base; + typedef datalog::udoc_relation udoc_relation; + typedef datalog::udoc_plugin udoc_plugin; + typedef datalog::relation_signature relation_signature; + typedef datalog::relation_fact relation_fact; + typedef scoped_ptr rel_mut; + typedef scoped_ptr rel_union; + + struct init { + init(ast_manager& m) { + reg_decl_plugins(m); + } + }; + random_gen m_rand; + ast_manager m; + init m_init; + bv_util bv; + expr_ref_vector m_vars; + smt_params m_smt_params; + datalog::register_engine m_reg; + datalog::context m_ctx; + datalog::rel_context rc; + udoc_plugin& p; + datalog::check_relation_plugin& cr; + + + tbit choose_tbit() { + switch (m_rand(3)) { + case 0: return BIT_0; + case 1: return BIT_1; + default : return BIT_x; + } + } + + tbv* mk_rand_tbv(doc_manager& dm) { + tbv* result = dm.tbvm().allocate(); + for (unsigned i = 0; i < dm.num_tbits(); ++i) { + dm.tbvm().set(*result, i, choose_tbit()); + } + return result; + } + + tbv* mk_rand_tbv(doc_manager& dm, tbv const& pos) { + tbv* result = dm.tbvm().allocate(); + for (unsigned i = 0; i < dm.num_tbits(); ++i) { + if (pos[i] == BIT_x) { + dm.tbvm().set(*result, i, choose_tbit()); + } + else { + dm.tbvm().set(*result, i, pos[i]); + } + } + return result; + } + + doc* mk_rand_doc(doc_manager& dm, unsigned num_diff) { + tbv_ref t(dm.tbvm()); + doc_ref result(dm); + t = mk_rand_tbv(dm); + result = dm.allocate(*t); + SASSERT(dm.tbvm().equals(*t, result->pos())); + for (unsigned i = 0; i < num_diff; ++i) { + t = mk_rand_tbv(dm, result->pos()); + if (dm.tbvm().equals(*t, result->pos())) { + return 0; + } + if (!result->neg().is_empty() && + dm.tbvm().equals(*t, result->neg()[0])) { + continue; + } + result->neg().push_back(t.detach()); + } + SASSERT(dm.well_formed(*result)); + return result.detach(); + } + + void mk_rand_udoc(doc_manager& dm, unsigned num_elems, unsigned num_diff, udoc& result) { + result.reset(dm); + for (unsigned i = 0; i < num_elems; ++i) { + doc* d = mk_rand_doc(dm, num_diff); + if (d) { + result.push_back(d); + } + } + } + +public: + udoc_tester(): + m_init(m), bv(m), m_vars(m), m_ctx(m, m_reg, m_smt_params), rc(m_ctx), + p(dynamic_cast(*rc.get_rmanager().get_relation_plugin(symbol("doc")))), + cr(dynamic_cast(*rc.get_rmanager().get_relation_plugin(symbol("check_relation")))) + { + cr.set_plugin(&p); + } + + udoc_relation* mk_empty(relation_signature const& sig) { + SASSERT(p.can_handle_signature(sig)); + relation_base* empty = p.mk_empty(sig); + return dynamic_cast(empty); + } + + udoc_relation* mk_full(relation_signature const& sig) { + func_decl_ref fn(m); + fn = m.mk_func_decl(symbol("full"), sig.size(), sig.c_ptr(), m.mk_bool_sort()); + relation_base* full = p.mk_full(fn, sig); + return dynamic_cast(full); + } + + void test1() { + datalog::relation_signature sig; + sig.push_back(bv.mk_sort(12)); + sig.push_back(bv.mk_sort(6)); + sig.push_back(bv.mk_sort(12)); + + + datalog::relation_fact fact1(m), fact2(m), fact3(m); + fact1.push_back(bv.mk_numeral(rational(1), 12)); + fact1.push_back(bv.mk_numeral(rational(6), 6)); + fact1.push_back(bv.mk_numeral(rational(56), 12)); + fact2.push_back(bv.mk_numeral(rational(8), 12)); + fact2.push_back(bv.mk_numeral(rational(16), 6)); + fact2.push_back(bv.mk_numeral(rational(32), 12)); + fact3.push_back(bv.mk_numeral(rational(32), 12)); + fact3.push_back(bv.mk_numeral(rational(16), 6)); + fact3.push_back(bv.mk_numeral(rational(4), 12)); + + relation_signature sig2; + sig2.push_back(bv.mk_sort(3)); + sig2.push_back(bv.mk_sort(6)); + sig2.push_back(bv.mk_sort(3)); + sig2.push_back(bv.mk_sort(3)); + sig2.push_back(bv.mk_sort(3)); + + relation_base* t; + udoc_relation* t1, *t2, *t3; + expr_ref fml(m); + + test_filter_neg2(); + + test_join_project(); + test_join_project2(); + + test_filter_neg4(false); + test_filter_neg4(true); + test_filter_neg5(false); + test_filter_neg5(true); + + test_filter_neg(); + test_filter_neg3(); + + test_join(1000); + + test_rename(); + + + // empty + { + std::cout << "empty\n"; + t = mk_empty(sig); + t->display(std::cout); std::cout << "\n"; + t->to_formula(fml); + std::cout << fml << "\n"; + t->deallocate(); + } + + // full + { + std::cout << "full\n"; + t = mk_full(sig); + t->display(std::cout); std::cout << "\n"; + t->to_formula(fml); + std::cout << fml << "\n"; + t->deallocate(); + } + + // join + { + t1 = mk_full(sig); + t2 = mk_full(sig); + t3 = mk_empty(sig); + unsigned_vector jc1, jc2; + jc1.push_back(1); + jc2.push_back(1); + datalog::relation_join_fn* join_fn = p.mk_join_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr()); + SASSERT(join_fn); + t = (*join_fn)(*t1, *t2); + cr.verify_join(*t1, *t2, *t, jc1, jc2); + t->display(std::cout); std::cout << "\n"; + t->deallocate(); + + t = (*join_fn)(*t1, *t3); + cr.verify_join(*t1, *t3, *t, jc1, jc2); + SASSERT(t->empty()); + t->display(std::cout); std::cout << "\n"; + t->deallocate(); + + t = (*join_fn)(*t3, *t3); + cr.verify_join(*t3, *t3, *t, jc1, jc2); + SASSERT(t->empty()); + t->display(std::cout); std::cout << "\n"; + t->deallocate(); + + dealloc(join_fn); + t1->deallocate(); + t2->deallocate(); + t3->deallocate(); + } + + // project + { + std::cout << "project\n"; + t1 = mk_full(sig); + unsigned_vector pc; + pc.push_back(0); + datalog::relation_transformer_fn* proj_fn = p.mk_project_fn(*t1, pc.size(), pc.c_ptr()); + t = (*proj_fn)(*t1); + cr.verify_project(*t1, *t, pc); + t->display(std::cout); std::cout << "\n"; + t->deallocate(); + + t1->reset(); + t = (*proj_fn)(*t1); + cr.verify_project(*t1, *t, pc); + t->display(std::cout); std::cout << "\n"; + t->deallocate(); + + t1->add_fact(fact1); + t1->add_fact(fact2); + t1->add_fact(fact3); + t = (*proj_fn)(*t1); + cr.verify_project(*t1, *t, pc); + t1->display(std::cout); std::cout << "\n"; + t->display(std::cout); std::cout << "\n"; + t->deallocate(); + + dealloc(proj_fn); + t1->deallocate(); + } + + // union + { + t1 = mk_empty(sig); + t2 = mk_empty(sig); + udoc_relation* delta = mk_full(sig); + t2->add_fact(fact1); + t2->add_fact(fact2); + t1->add_fact(fact3); + + expr_ref t10(m); + t1->to_formula(t10); + expr_ref delta0(m); + delta->to_formula(delta0); + rel_union union_fn = p.mk_union_fn(*t1, *t2, 0); + + t1->display(std::cout << "t1 before:"); std::cout << "\n"; + (*union_fn)(*t1, *t2, delta); + cr.verify_union(t10, *t2, *t1, delta0, delta); + t1->display(std::cout << "t1 after:"); std::cout << "\n"; + delta->display(std::cout << "delta:"); std::cout << "\n"; + + t1->deallocate(); + t2->deallocate(); + delta->deallocate(); + } + + // filter_identical + { + t1 = mk_empty(sig2); + unsigned_vector id; + id.push_back(0); + id.push_back(2); + id.push_back(4); + rel_mut filter_id = p.mk_filter_identical_fn(*t1, id.size(), id.c_ptr()); + relation_fact f1(m); + f1.push_back(bv.mk_numeral(rational(1),3)); + f1.push_back(bv.mk_numeral(rational(1),6)); + f1.push_back(bv.mk_numeral(rational(1),3)); + f1.push_back(bv.mk_numeral(rational(1),3)); + f1.push_back(bv.mk_numeral(rational(1),3)); + t1->add_fact(f1); + f1[4] = bv.mk_numeral(rational(2),3); + t1->add_fact(f1); + t1->display(std::cout); std::cout << "\n"; + (*filter_id)(*t1); + t1->display(std::cout); std::cout << "\n"; + t1->deallocate(); + } + + // tbv_manager::debug_alloc(); + { + relation_signature sig3; + sig3.push_back(m.mk_bool_sort()); + sig3.push_back(m.mk_bool_sort()); + sig3.push_back(m.mk_bool_sort()); + var_ref v0(m.mk_var(0, m.mk_bool_sort()),m); + var_ref v1(m.mk_var(1, m.mk_bool_sort()),m); + var_ref v2(m.mk_var(2, m.mk_bool_sort()),m); + app_ref cond1(m); + t1 = mk_full(sig3); + cond1 = m.mk_eq(v0,v1); + apply_filter(*t1, cond1); + t1->deallocate(); + } + + { + relation_signature sig3; + sig3.push_back(m.mk_bool_sort()); + sig3.push_back(m.mk_bool_sort()); + sig3.push_back(m.mk_bool_sort()); + var_ref v0(m.mk_var(0, m.mk_bool_sort()),m); + var_ref v1(m.mk_var(1, m.mk_bool_sort()),m); + var_ref v2(m.mk_var(2, m.mk_bool_sort()),m); + app_ref cond1(m); + t1 = mk_full(sig3); + cond1 = m.mk_or(m.mk_eq(v0,v1),m.mk_eq(v0,v2)); + apply_filter(*t1, cond1); + t1->deallocate(); + } + + { + relation_signature sig3; + sig3.push_back(bv.mk_sort(1)); + sig3.push_back(bv.mk_sort(1)); + sig3.push_back(bv.mk_sort(1)); + var_ref v0(m.mk_var(0, bv.mk_sort(1)),m); + var_ref v1(m.mk_var(1, bv.mk_sort(1)),m); + var_ref v2(m.mk_var(2, bv.mk_sort(1)),m); + app_ref cond1(m); + cond1 = m.mk_or(m.mk_eq(v0,v1),m.mk_eq(v0,v2)); + t1 = mk_full(sig3); + apply_filter(*t1, cond1); + t1->deallocate(); + } + + + app_ref_vector conds(m); + app_ref cond1(m); + var_ref v0(m.mk_var(0, bv.mk_sort(3)),m); + var_ref v1(m.mk_var(1, bv.mk_sort(6)),m); + var_ref v2(m.mk_var(2, bv.mk_sort(3)),m); + var_ref v3(m.mk_var(3, bv.mk_sort(3)),m); + var_ref v4(m.mk_var(4, bv.mk_sort(3)),m); + conds.push_back(m.mk_true()); + conds.push_back(m.mk_false()); + conds.push_back(m.mk_eq(v0, v2)); + conds.push_back(m.mk_not(m.mk_eq(v0, v2))); + conds.push_back(m.mk_eq(v0, bv.mk_numeral(rational(2), 3))); + cond1 = m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)); + conds.push_back(cond1); + conds.push_back(m.mk_or(cond1,m.mk_eq(v3,v4))); + conds.push_back(m.mk_eq(ex(2,1,v3),ex(1,0,v4))); + conds.push_back(m.mk_or(cond1,m.mk_eq(ex(2,1,v3),ex(1,0,v4)))); + conds.push_back(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v0,v4))); + conds.push_back(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v3,v4))); + conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),ex(1,0,v2)),m.mk_eq(v3,v4))); + conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)), + m.mk_eq(v3,v4))); + conds.push_back(m.mk_or(m.mk_eq(ex(2,1,v0),bv.mk_numeral(rational(3),2)), + m.mk_eq(v3,bv.mk_numeral(rational(3),3)))); + conds.push_back(m.mk_or(m.mk_eq(v0,bv.mk_numeral(rational(5),3)), + m.mk_eq(v3,bv.mk_numeral(rational(5),3)))); + conds.push_back(m.mk_or(m.mk_eq(v0,bv.mk_numeral(rational(7),3)), + m.mk_eq(v3,bv.mk_numeral(rational(7),3)))); + conds.push_back(m.mk_not(m.mk_or(m.mk_eq(v0,v2),m.mk_eq(v3,v4)))); + + + // filter_interpreted + { + std::cout << "filter interpreted\n"; + t1 = mk_full(sig2); + + for (unsigned i = 0; i < conds.size(); ++i) { + apply_filter(*t1, conds[i].get()); + } + + t1->deallocate(); + + } + + // filter_interpreted_project + { + unsigned_vector remove; + remove.push_back(0); + remove.push_back(2); + + t1 = mk_full(sig2); + apply_filter(*t1, conds[2].get()); + apply_filter_project(*t1, remove, conds[2].get()); + apply_filter_project(*t1, remove, conds[3].get()); + t1->deallocate(); + + t1 = mk_full(sig2); + apply_filter(*t1, conds[3].get()); + apply_filter_project(*t1, remove, conds[2].get()); + apply_filter_project(*t1, remove, conds[3].get()); + t1->deallocate(); + + for (unsigned i = 0; i < conds.size(); ++i) { + t1 = mk_full(sig2); + apply_filter_project(*t1, remove, conds[i].get()); + t1->deallocate(); + } + + remove[1] = 1; + for (unsigned i = 0; i < conds.size(); ++i) { + t1 = mk_full(sig2); + apply_filter_project(*t1, remove, conds[i].get()); + t1->deallocate(); + } + } + + + } + + // {11xx \ {111x}, x011 \ {x011}, 0111} + // {xx11 \ {0011, 1111, x111}} + // 0111 + // {1x1x \ {1x1x}, 1111 \ {1111}, x1x1 \ {x1x1}} + + void test_join_project() + { + datalog::relation_signature sig; + sig.push_back(bv.mk_sort(2)); + sig.push_back(bv.mk_sort(2)); + //sig.push_back(bv.mk_sort(3)); + + unsigned_vector jc1, jc2, pc; + jc1.push_back(0); + jc2.push_back(0); + pc.push_back(1); + pc.push_back(3); + //pc.push_back(4); + udoc_relation* t1, *t2; + relation_base* t; + + scoped_ptr join_project_fn; + + for (unsigned i = 0; i < 200; ++i) { + t1 = mk_rand(sig); + t2 = mk_rand(sig); + t1->display(std::cout); + t2->display(std::cout); + join_project_fn = p.mk_join_project_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr(), pc.size(), pc.c_ptr()); + t = (*join_project_fn)(*t1, *t2); + t->display(std::cout); + cr.verify_join_project(*t1, *t2, *t, jc1, jc2, pc); + t->deallocate(); + t1->deallocate(); + t2->deallocate(); + } + } + + void test_join_project2() + { + relation_signature sig3; + sig3.push_back(bv.mk_sort(1)); + sig3.push_back(bv.mk_sort(1)); + sig3.push_back(bv.mk_sort(1)); + + /// xxx \ x11 + udoc_relation *t1 = mk_full(sig3); + { + udoc_relation *neg = mk_full(sig3); + doc& n = neg->get_udoc()[0]; + neg->get_dm().set(n, 1, BIT_1); + neg->get_dm().set(n, 2, BIT_1); + + unsigned_vector allcols; + allcols.push_back(0); + allcols.push_back(1); + allcols.push_back(2); + apply_filter_neg(*t1, *neg, allcols, allcols); + neg->deallocate(); + } + + // 11x + udoc_relation *t2 = mk_full(sig3); + { + doc& n = t2->get_udoc()[0]; + t2->get_dm().set(n, 0, BIT_1); + t2->get_dm().set(n, 1, BIT_1); + } + + unsigned_vector jc1, jc2, pc; + jc1.push_back(1); + jc1.push_back(2); + jc2.push_back(0); + jc2.push_back(1); + pc.push_back(1); + pc.push_back(2); + + scoped_ptr join_project_fn; + join_project_fn = p.mk_join_project_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr(), pc.size(), pc.c_ptr()); + relation_base *t = (*join_project_fn)(*t1, *t2); + cr.verify_join_project(*t1, *t2, *t, jc1, jc2, pc); + t->deallocate(); + t1->deallocate(); + t2->deallocate(); + } + + void test_rename() { + udoc_relation* t1; + // rename + datalog::relation_signature sig; + sig.push_back(bv.mk_sort(12)); + sig.push_back(bv.mk_sort(6)); + sig.push_back(bv.mk_sort(2)); + datalog::relation_fact fact1(m); + fact1.push_back(bv.mk_numeral(rational(1), 12)); + fact1.push_back(bv.mk_numeral(rational(6), 6)); + fact1.push_back(bv.mk_numeral(rational(3), 2)); + t1 = mk_empty(sig); + t1->add_fact(fact1); + unsigned_vector cycle; + cycle.push_back(0); + cycle.push_back(2); + check_permutation(t1, cycle); + + sig.reset(); + sig.push_back(bv.mk_sort(2)); + sig.push_back(bv.mk_sort(6)); + sig.push_back(bv.mk_sort(12)); + fact1.reset(); + fact1.push_back(bv.mk_numeral(rational(3), 2)); + fact1.push_back(bv.mk_numeral(rational(6), 6)); + fact1.push_back(bv.mk_numeral(rational(1), 12)); + t1 = mk_empty(sig); + t1->add_fact(fact1); + cycle.reset(); + cycle.push_back(0); + cycle.push_back(2); + check_permutation(t1, cycle); + + t1 = mk_empty(sig); + t1->add_fact(fact1); + cycle.reset(); + cycle.push_back(0); + cycle.push_back(1); + cycle.push_back(2); + check_permutation(t1, cycle); + } + + void test_join(unsigned num_rounds) { + for (unsigned i = 0; i < num_rounds; ++i) { + test_join(); + } + } + + void test_join() { + relation_signature sig; + sig.push_back(bv.mk_sort(2)); + sig.push_back(bv.mk_sort(3)); + udoc_relation* t1, *t2; + relation_base* t; + + t1 = mk_rand(sig); + t2 = mk_rand(sig); + + unsigned_vector jc1, jc2; + jc1.push_back(0); + jc2.push_back(0); + scoped_ptr join_fn; + + join_fn = p.mk_join_fn(*t1, *t2, jc1.size(), jc1.c_ptr(), jc2.c_ptr()); + t = (*join_fn)(*t1, *t2); + + cr.verify_join(*t1, *t2, *t, jc1, jc2); + t1->display(std::cout); + t2->display(std::cout); + t->display(std::cout); + std::cout << "\n"; + t1->deallocate(); + t2->deallocate(); + t->deallocate(); + } + + udoc_relation* mk_rand(relation_signature const& sig) { + udoc_relation* t = mk_empty(sig); + mk_rand_udoc(t->get_dm(), 3, 3, t->get_udoc()); + return t; + } + + void check_permutation(relation_base* t1, unsigned_vector const& cycle) { + scoped_ptr rename; + rename = p.mk_rename_fn(*t1, cycle.size(), cycle.c_ptr()); + relation_base* t = (*rename)(*t1); + cr.verify_permutation(*t1,*t, cycle); + t1->display(std::cout); std::cout << "\n"; + t->display(std::cout); std::cout << "\n"; + t->deallocate(); + t1->deallocate(); + } + + /* + 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 test_filter_neg() { + // filter_by_negation + + relation_signature sig4; + sig4.push_back(bv.mk_sort(1)); + sig4.push_back(bv.mk_sort(1)); + sig4.push_back(bv.mk_sort(1)); + udoc_relation* t1 = mk_empty(sig4); + udoc_relation* t2 = mk_empty(sig4); + unsigned_vector cols1, cols2; + unsigned num_bits = t1->get_dm().num_tbits(); + + cols1.push_back(0); + cols2.push_back(1); + for (unsigned i = 0; i < 100; ++i) { + set_random(*t1, 2*num_bits/3); + set_random(*t2, 2*num_bits/3); + apply_filter_neg(*t1,*t2, cols1, cols2); + } + cols1.push_back(1); + cols2.push_back(2); + for (unsigned i = 0; i < 200; ++i) { + set_random(*t1, 2*num_bits/3); + set_random(*t2, 2*num_bits/3); + apply_filter_neg(*t1,*t2, cols1, cols2); + } + t1->deallocate(); + t2->deallocate(); + } + + void test_filter_neg2() { + // filter_by_negation + relation_signature sig4; + sig4.push_back(bv.mk_sort(1)); + sig4.push_back(bv.mk_sort(1)); + sig4.push_back(bv.mk_sort(1)); + unsigned_vector cols, allcols; + + cols.push_back(0); + cols.push_back(2); + allcols.push_back(0); + allcols.push_back(1); + allcols.push_back(2); + + /// xxx \ 1x0 + udoc_relation* t1 = mk_full(sig4); + { + udoc_relation* neg = mk_full(sig4); + doc& n = neg->get_udoc()[0]; + neg->get_dm().set(n, 0, BIT_1); + neg->get_dm().set(n, 2, BIT_0); + apply_filter_neg(*t1, *neg, allcols, allcols); + neg->deallocate(); + } + + /// xxx \ (1x1 u 0x0) + udoc_relation* t2 = mk_full(sig4); + { + udoc_relation* neg = mk_full(sig4); + doc& n = neg->get_udoc()[0]; + neg->get_dm().set(n, 0, BIT_0); + neg->get_dm().set(n, 2, BIT_0); + apply_filter_neg(*t2, *neg, allcols, allcols); + neg->deallocate(); + } + { + udoc_relation* neg = mk_full(sig4); + doc& n = neg->get_udoc()[0]; + neg->get_dm().set(n, 0, BIT_1); + neg->get_dm().set(n, 2, BIT_1); + apply_filter_neg(*t2, *neg, allcols, allcols); + neg->deallocate(); + } + t1->display(std::cout); + t2->display(std::cout); + + apply_filter_neg(*t2, *t1, cols, cols); + t1->deallocate(); + t2->deallocate(); + } + + void test_filter_neg3() { + // filter_by_negation + relation_signature sig; + sig.push_back(bv.mk_sort(1)); + sig.push_back(bv.mk_sort(1)); + sig.push_back(bv.mk_sort(1)); + unsigned_vector cols1, cols2; + + cols1.push_back(0); + cols1.push_back(0); + cols2.push_back(0); + cols2.push_back(1); + + /// 1xx + udoc_relation* t1 = mk_full(sig); + { + doc& d = t1->get_udoc()[0]; + t1->get_dm().set(d, 0, BIT_1); + } + + /// 10x + udoc_relation* t2 = mk_full(sig); + { + doc& d = t2->get_udoc()[0]; + t1->get_dm().set(d, 0, BIT_1); + t1->get_dm().set(d, 1, BIT_0); + } + + apply_filter_neg(*t1, *t2, cols1, cols2); + t1->deallocate(); + t2->deallocate(); + } + + void test_filter_neg4(bool disable_fast) { + relation_signature sig1, sig2; + sig1.push_back(bv.mk_sort(2)); + sig1.push_back(bv.mk_sort(2)); + sig2.push_back(bv.mk_sort(2)); + unsigned_vector cols1, cols2; + + cols1.push_back(0); + cols1.push_back(1); + cols2.push_back(0); + cols2.push_back(0); + udoc_relation* tgt = mk_full(sig1); + udoc_relation* neg = mk_full(sig2); + if (disable_fast) p.disable_fast_pass(); + apply_filter_neg(*tgt, *neg, cols1, cols2); + tgt->deallocate(); + + tgt = mk_full(sig1); + apply_filter_neg(*neg, *tgt, cols2, cols1); + tgt->deallocate(); + neg->deallocate(); + } + + void test_filter_neg5(bool disable_fast) { + relation_signature sig1, sig2; + sig1.push_back(bv.mk_sort(2)); + sig1.push_back(bv.mk_sort(2)); + sig2.push_back(bv.mk_sort(2)); + sig2.push_back(bv.mk_sort(2)); + sig2.push_back(bv.mk_sort(2)); + unsigned_vector cols1, cols2, cols3; + + cols1.push_back(0); + cols1.push_back(1); + cols2.push_back(0); + cols2.push_back(2); + cols3.push_back(0); + cols3.push_back(1); + udoc_relation* tgt = mk_full(sig1); + udoc_relation* neg = mk_full(sig2); + rel_mut filter_id = p.mk_filter_identical_fn(*tgt, cols3.size(), cols3.c_ptr()); + (*filter_id)(*tgt); + if (disable_fast) p.disable_fast_pass(); + apply_filter_neg(*tgt, *neg, cols1, cols2); + tgt->deallocate(); + neg->deallocate(); + } + + void set_random(udoc_relation& r, unsigned num_vals) { + unsigned num_bits = r.get_dm().num_tbits(); + udoc_relation* full = mk_full(r.get_signature()); + rel_union union_fn = p.mk_union_fn(r, r, 0); + (*union_fn)(r, *full); + doc_manager& dm = r.get_dm(); + SASSERT(r.get_udoc().size() == 1); + doc& d0 = r.get_udoc()[0]; + SASSERT(dm.is_full(d0)); + for (unsigned i = 0; i < num_vals; ++i) { + unsigned idx = m_rand(num_bits); + unsigned val = m_rand(2); + tbit b = (val == 0)?BIT_0:BIT_1; + dm.set(d0, idx, b); + } + full->deallocate(); + } + + void apply_filter_neg(udoc_relation& dst, udoc_relation& neg, + unsigned_vector const& cols1, unsigned_vector const& cols2) { + + scoped_ptr negf; + negf = p.mk_filter_by_negation_fn(dst, neg, cols1.size(), cols1.c_ptr(), cols2.c_ptr()); + expr_ref dst0(m); + dst.to_formula(dst0); + (*negf)(dst, neg); + cr.verify_filter_by_negation(dst0, dst, neg, cols1, cols2); + + /* + + tgt_1:={ x: x\in tgt_0 && ! \exists y: + ( y \in neg & pi_c1(x)= pi_d1(y) & ... & pi_cN(x)= pi_dN(y) ) } + */ + } + + expr_ref ex(unsigned hi, unsigned lo, expr* e) { + expr_ref result(m); + result = bv.mk_extract(hi, lo, e); + return result; + } + + void apply_filter_project(udoc_relation& t, unsigned_vector const& rm, app* cond) { + scoped_ptr rt; + rt = p.mk_filter_interpreted_and_project_fn(t, cond, rm.size(), rm.c_ptr()); + datalog::relation_base* result = (*rt)(t); + cr.verify_filter_project(t, *result, cond, rm); + result->deallocate(); + } + + void project_var(unsigned i, sort* s, expr_ref& fml) { + var_ref v(m); + v = m.mk_var(i, s); + unsigned num_bits = bv.get_bv_size(s); + unsigned p = 1 << num_bits; + expr_ref_vector disj(m); + expr_ref tmp(m); + for (unsigned i = 0; i < p; ++i) { + expr_safe_replace repl(m); + repl.insert(v, bv.mk_numeral(rational(i), s)); + tmp = fml; + repl(tmp); + disj.push_back(tmp); + } + fml = mk_or(m, disj.size(), disj.c_ptr()); + } + + void apply_filter(udoc_relation& t, app* cond) { + udoc_relation* full = mk_full(t.get_signature()); + rel_union union_fn = p.mk_union_fn(t, *full, 0); + (*union_fn)(t, *full, 0); + expr_ref fml0(m); + t.to_formula(fml0); + rel_mut fint = p.mk_filter_interpreted_fn(t, cond); + (*fint)(t); + t.display(std::cout << "filter: " << mk_pp(cond, m) << " "); std::cout << "\n"; + cr.verify_filter(fml0, t, cond); + full->deallocate(); + } + + +}; + +void tst_udoc_relation() { + udoc_tester tester; + + try { + tester.test1(); + } + catch (z3_exception& ex) { + std::cout << ex.msg() << "\n"; + } +} diff --git a/src/util/bit_util.cpp b/src/util/bit_util.cpp index 0f1fd294d..fde66b054 100644 --- a/src/util/bit_util.cpp +++ b/src/util/bit_util.cpp @@ -351,7 +351,7 @@ bool has_one_at_first_k_bits(unsigned sz, unsigned const * data, unsigned k) { } if (word_sz < sz) { unsigned bit_sz = k % (8 * sizeof(unsigned)); - unsigned mask = (1 << bit_sz) - 1; + unsigned mask = (1u << bit_sz) - 1; return (data[word_sz] & mask) != 0; } return false; diff --git a/src/util/bit_vector.cpp b/src/util/bit_vector.cpp index cf96cb295..c15a743a9 100644 --- a/src/util/bit_vector.cpp +++ b/src/util/bit_vector.cpp @@ -206,6 +206,34 @@ void bit_vector::display(std::ostream & out) const { #endif } +bool bit_vector::contains(bit_vector const& other) const { + unsigned n = num_words(); + if (n == 0) + return true; + + for (unsigned i = 0; i < n - 1; ++i) { + if ((m_data[i] & other.m_data[i]) != other.m_data[i]) + return false; + } + unsigned bit_rest = m_num_bits % 32; + unsigned mask = (1U << bit_rest) - 1; + if (mask == 0) mask = UINT_MAX; + unsigned other_data = other.m_data[n-1] & mask; + return (m_data[n-1] & other_data) == other_data; +} + +unsigned bit_vector::get_hash() const { + return string_hash(reinterpret_cast(m_data), size()/8, 0); +} + +bit_vector& bit_vector::neg() { + unsigned n = num_words(); + for (unsigned i = 0; i < n; ++i) { + m_data[i] = ~m_data[i]; + } + return *this; +} + void fr_bit_vector::reset() { unsigned sz = size(); unsigned_vector::const_iterator it = m_one_idxs.begin(); diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index 6738ba0aa..f3fd7c27b 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -75,8 +75,11 @@ public: bit_vector(bit_vector const & source): m_num_bits(source.m_num_bits), m_capacity(source.m_capacity), - m_data(alloc_svect(unsigned, source.m_capacity)) { - memcpy(m_data, source.m_data, source.m_capacity * sizeof(unsigned)); + m_data(0) { + if (source.m_data) { + m_data = alloc_svect(unsigned, m_capacity); + memcpy(m_data, source.m_data, m_capacity * sizeof(unsigned)); + } } bit_vector(unsigned const * source, int num_bits): @@ -91,7 +94,8 @@ public: } void reset() { - memset(m_data, 0, m_capacity * sizeof(unsigned)); + if (m_data) + memset(m_data, 0, m_capacity * sizeof(unsigned)); m_num_bits = 0; } @@ -123,6 +127,8 @@ public: unsigned get_word(unsigned word_idx) const { return m_data[word_idx]; } + + unsigned get_hash() const; bool get(unsigned bit_idx) const { SASSERT(bit_idx < size()); @@ -184,6 +190,9 @@ public: bit_vector & operator=(bit_vector const & source) { m_num_bits = source.m_num_bits; + if (!source.m_data) + return *this; + if (m_capacity < source.m_capacity) { dealloc_svect(m_data); m_data = alloc_svect(unsigned, source.m_capacity); @@ -196,8 +205,13 @@ public: bit_vector & operator|=(bit_vector const & source); bit_vector & operator&=(bit_vector const & source); + + bit_vector & neg(); void display(std::ostream & out) const; + + bool contains(const bit_vector & other) const; + }; inline std::ostream & operator<<(std::ostream & out, bit_vector const & b) { diff --git a/src/util/dependency.h b/src/util/dependency.h index 6ce9b2025..33a0e4d22 100644 --- a/src/util/dependency.h +++ b/src/util/dependency.h @@ -171,7 +171,7 @@ public: m_todo.push_back(d); unsigned qhead = 0; while (qhead < m_todo.size()) { - dependency * d = m_todo[qhead]; + d = m_todo[qhead]; qhead++; if (d->is_leaf()) { if (to_leaf(d)->m_value == v) { @@ -201,7 +201,7 @@ public: m_todo.push_back(d); unsigned qhead = 0; while (qhead < m_todo.size()) { - dependency * d = m_todo[qhead]; + d = m_todo[qhead]; qhead++; if (d->is_leaf()) { vs.push_back(to_leaf(d)->m_value); diff --git a/src/util/fixed_bit_vector.cpp b/src/util/fixed_bit_vector.cpp new file mode 100644 index 000000000..8843db736 --- /dev/null +++ b/src/util/fixed_bit_vector.cpp @@ -0,0 +1,168 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + fixed_bit_vector.cpp + +Abstract: + + Simple bitvector implementation for fixed size bit-vectors. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-9-15. + Leonardo de Moura (leonardo) 2006-10-03. + +Revision History: + Based on bit_vector.cpp + +--*/ + +#include +#include"fixed_bit_vector.h" +#include"trace.h" +#include"hash.h" + +void fixed_bit_vector::set(fixed_bit_vector const& other, unsigned hi, unsigned lo) { + if ((lo % 32) == 0) { + unsigned sz32 = (hi-lo+1)/32; + unsigned lo32 = lo/32; + for (unsigned i = 0; i < sz32; ++i) { + m_data[lo32 + i] = other.m_data[i]; + } + for (unsigned i = sz32*32; i < hi - lo + 1; ++i) { + set(lo + i, other.get(i)); + } + return; + } + for (unsigned i = 0; i < hi - lo + 1; ++i) { + set(lo + i, other.get(i)); + } +} + +fixed_bit_vector_manager::fixed_bit_vector_manager(unsigned num_bits): + m_alloc("fixed_bit_vector") { + m_num_bits = num_bits; + m_num_words = num_words(num_bits); + m_num_bytes = m_num_words * sizeof(unsigned); + unsigned bit_rest = m_num_bits % 32; + m_mask = (1U << bit_rest) - 1; + if (m_mask == 0) m_mask = UINT_MAX; +} + + +fixed_bit_vector* fixed_bit_vector_manager::allocate() { + if (m_num_bytes == 0) return &m_0; + return static_cast(m_alloc.allocate(m_num_bytes)); +} + +fixed_bit_vector* fixed_bit_vector_manager::allocate0() { + fixed_bit_vector* result = allocate(); + fill0(*result); + return result; +} + +fixed_bit_vector* fixed_bit_vector_manager::allocate1() { + fixed_bit_vector* result = allocate(); + fill1(*result); + return result; +} + +fixed_bit_vector* fixed_bit_vector_manager::allocate(fixed_bit_vector const& bv) { + fixed_bit_vector* result = allocate(); + copy(*result, bv); + return result; +} + +void fixed_bit_vector_manager::deallocate(fixed_bit_vector* bv) { + if (m_num_bytes > 0) m_alloc.deallocate(m_num_bytes, bv); +} + + +void fixed_bit_vector_manager::copy(fixed_bit_vector& dst, fixed_bit_vector const& src) const { + memcpy(dst.m_data, src.m_data, num_bytes()); +} + + +fixed_bit_vector& +fixed_bit_vector_manager::fill0(fixed_bit_vector& bv) const { + memset(bv.m_data, 0, num_bytes()); + return bv; +} + +fixed_bit_vector& +fixed_bit_vector_manager::fill1(fixed_bit_vector& bv) const { + memset(bv.m_data, 0xFF, num_bytes()); + return bv; +} + +fixed_bit_vector& +fixed_bit_vector_manager::set_and(fixed_bit_vector& dst, fixed_bit_vector const& src) const { + for (unsigned i = 0; i < m_num_words; i++) + dst.m_data[i] &= src.m_data[i]; + return dst; +} + +fixed_bit_vector& +fixed_bit_vector_manager::set_or(fixed_bit_vector& dst, fixed_bit_vector const& src) const { + for (unsigned i = 0; i < m_num_words; i++) + dst.m_data[i] |= src.m_data[i]; + return dst; +} + +fixed_bit_vector& +fixed_bit_vector_manager::set_neg(fixed_bit_vector& dst) const { + for (unsigned i = 0; i < m_num_words; i++) + dst.m_data[i] = ~dst.m_data[i]; + return dst; +} + +unsigned fixed_bit_vector_manager::last_word(fixed_bit_vector const& bv) const { + unsigned n = num_words(); + if (n == 0) return 0; + return bv.m_data[n-1] & m_mask; +} + +bool fixed_bit_vector_manager::equals(fixed_bit_vector const& a, fixed_bit_vector const& b) const { + if (&a == &b) return true; + unsigned n = num_words(); + if (n == 0) + return true; + for (unsigned i = 0; i < n - 1; i++) { + if (a.m_data[i] != b.m_data[i]) + return false; + } + return last_word(a) == last_word(b); +} +unsigned fixed_bit_vector_manager::hash(fixed_bit_vector const& src) const { + return string_hash(reinterpret_cast(src.m_data), num_bits()/8, num_bits()); +} + +bool fixed_bit_vector_manager::contains(fixed_bit_vector const& a, fixed_bit_vector const& b) const { + unsigned n = num_words(); + if (n == 0) + return true; + + for (unsigned i = 0; i < n - 1; ++i) { + if ((a.m_data[i] & b.m_data[i]) != b.m_data[i]) + return false; + } + unsigned b_data = last_word(b); + return (last_word(a) & b_data) == b_data; +} + +std::ostream& fixed_bit_vector_manager::display(std::ostream& out, fixed_bit_vector const& b) const { + unsigned i = num_bits(); + while (i > 0) { + --i; + if (b.get(i)) + out << "1"; + else + out << "0"; + } + return out; +} + + + diff --git a/src/util/fixed_bit_vector.h b/src/util/fixed_bit_vector.h new file mode 100644 index 000000000..933299925 --- /dev/null +++ b/src/util/fixed_bit_vector.h @@ -0,0 +1,146 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + fixed_bit_vector.h + +Abstract: + + Simple bitvector implementation for fixed size bit-vectors. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-9-15. + +Revision History: + + Related to bit_vector, but is based on a manager. + +--*/ +#ifndef _FIXED_BIT_VECTOR_H_ +#define _FIXED_BIT_VECTOR_H_ + +#include +#include"debug.h" +#include"small_object_allocator.h" + +class fixed_bit_vector { + friend class fixed_bit_vector_manager; + friend class tbv_manager; + unsigned m_data[1]; + + static unsigned get_pos_mask(unsigned bit_idx) { + return 1 << (bit_idx % 32); + } + + + unsigned get_bit_word(unsigned bit_idx) const { + return m_data[bit_idx / 32]; + } + + unsigned & get_bit_word(unsigned bit_idx) { + return m_data[bit_idx / 32]; + } + +public: + + fixed_bit_vector() {} + + ~fixed_bit_vector() {} + + unsigned get_word(unsigned word_idx) const { return m_data[word_idx]; } + + bool operator[](unsigned bit_idx) const { + return get(bit_idx); + } + + bool get(unsigned bit_idx) const { + return (get_bit_word(bit_idx) & get_pos_mask(bit_idx)) != 0; + } + +private: + void set(unsigned bit_idx) { + get_bit_word(bit_idx) |= get_pos_mask(bit_idx); + } + + void unset(unsigned bit_idx) { + get_bit_word(bit_idx) &= ~get_pos_mask(bit_idx); + } + + void set(unsigned bit_idx, bool val) { + int _val = static_cast(val); + get_bit_word(bit_idx) ^= (-_val ^ get_bit_word(bit_idx)) & get_pos_mask(bit_idx); + } + + // assign bits this[lo:hi] := other[0:hi-lo+1] + void set(fixed_bit_vector const& other, unsigned hi, unsigned lo); + +}; + +class fixed_bit_vector_manager { + friend class fixed_bit_vector; + small_object_allocator m_alloc; + unsigned m_num_bits; + unsigned m_num_bytes; + unsigned m_num_words; + unsigned m_mask; + fixed_bit_vector m_0; + + static unsigned num_words(unsigned num_bits) { + return (num_bits + 31) / 32; + } + +public: + fixed_bit_vector_manager(unsigned num_bits); + + void reset() { m_alloc.reset(); } + fixed_bit_vector* allocate(); + fixed_bit_vector* allocate1(); + fixed_bit_vector* allocate0(); + fixed_bit_vector* allocate(fixed_bit_vector const& bv); + void deallocate(fixed_bit_vector* bv); + + void copy(fixed_bit_vector& dst, fixed_bit_vector const& src) const; + unsigned num_words() const { return m_num_words; } + unsigned num_bytes() const { return m_num_bytes; } + unsigned num_bits() const { return m_num_bits; } + fixed_bit_vector& reset(fixed_bit_vector& bv) const { return fill0(bv); } + fixed_bit_vector& fill0(fixed_bit_vector& bv) const; + fixed_bit_vector& fill1(fixed_bit_vector& bv) const; + fixed_bit_vector& set_and(fixed_bit_vector& dst, fixed_bit_vector const& src) const; + fixed_bit_vector& set_or(fixed_bit_vector& dst, fixed_bit_vector const& src) const; + fixed_bit_vector& set_neg(fixed_bit_vector& dst) const; + unsigned last_word(fixed_bit_vector const& bv) const; + unsigned get_mask() const { return m_mask; } + bool equals(fixed_bit_vector const& a, fixed_bit_vector const& b) const; + unsigned hash(fixed_bit_vector const& src) const; + bool contains(fixed_bit_vector const& a, fixed_bit_vector const& b) const; + std::ostream& display(std::ostream& out, fixed_bit_vector const& b) const; + void set(fixed_bit_vector& dst, unsigned bit_idx) { + SASSERT(bit_idx < num_bits()); + dst.set(bit_idx); + } + void unset(fixed_bit_vector& dst, unsigned bit_idx) { + SASSERT(bit_idx < num_bits()); + dst.unset(bit_idx); + } + + void set(fixed_bit_vector& dst, unsigned bit_idx, bool val) { + SASSERT(bit_idx < num_bits()); + dst.set(bit_idx, val); + } + + // assign bits this[lo:hi] := other[0:hi-lo+1] + void set(fixed_bit_vector& dst, fixed_bit_vector const& other, unsigned hi, unsigned lo) { + SASSERT(lo <= hi && hi < num_bits()); + dst.set(other, hi, lo); + } + +}; + + + + +#endif /* _FIXED_BIT_VECTOR_H_ */ + diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index adffe6bde..69f596c5d 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -266,6 +266,7 @@ public: } } + void set(param_descrs const & d, symbol const & param_name, char const * value, symbol const & mod_name) { param_kind k = d.get_kind(param_name); params_ref & ps = get_params(mod_name); diff --git a/src/util/hash.cpp b/src/util/hash.cpp index 8b7530376..bb5ca0a41 100644 --- a/src/util/hash.cpp +++ b/src/util/hash.cpp @@ -19,11 +19,18 @@ Revision History: #include"debug.h" #include"hash.h" +#include + +static unsigned read_unsigned(const char *s) { + unsigned n; + memcpy(&n, s, sizeof(unsigned)); + return n; +} // I'm using Bob Jenkin's hash function. // http://burtleburtle.net/bob/hash/doobs.html unsigned string_hash(const char * str, unsigned length, unsigned init_value) { - register unsigned a, b, c, len; + unsigned a, b, c, len; /* Set up the internal state */ len = length; @@ -31,10 +38,11 @@ unsigned string_hash(const char * str, unsigned length, unsigned init_value) { c = init_value; /* the previous hash value */ /*---------------------------------------- handle most of the key */ + SASSERT(sizeof(unsigned) == 4); while (len >= 12) { - a += reinterpret_cast(str)[0]; - b += reinterpret_cast(str)[1]; - c += reinterpret_cast(str)[2]; + a += read_unsigned(str); + b += read_unsigned(str+4); + c += read_unsigned(str+8); mix(a,b,c); str += 12; len -= 12; } diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 40d20b644..21f1c281f 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -16,7 +16,9 @@ Author: Revision History: --*/ +#include #include +#include #ifdef _WINDOWS #pragma float_control( except, on ) // exception semantics; this does _not_ mean that exceptions are enabled (we want them off!) @@ -24,19 +26,13 @@ Revision History: #pragma fp_contract(off) // contractions off (`contraction' means x*y+z is turned into a fused-mul-add). #pragma fenv_access(on) // fpu environment sensitivity (needed to be allowed to make FPU mode changes). #else -#ifdef _WINDOWS -#pragma STDC FENV_ACCESS ON -#endif -#include -#include +#include #endif #ifndef _M_IA64 #define USE_INTRINSICS #endif -#include - #include"hwf.h" // Note: @@ -57,7 +53,7 @@ Revision History: hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) -{ +{ #ifdef _WINDOWS #if defined(_AMD64_) || defined(_M_IA64) // Precision control is not supported on x64. @@ -306,20 +302,31 @@ void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & #pragma fp_contract(on) #endif -void hwf_manager::fused_mul_add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o) { +void hwf_manager::fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o) { // CMW: fused_mul_add is not available on most CPUs. As of 2012, only Itanium, // Intel Sandybridge and AMD Bulldozers support that (via AVX). -#ifdef _M_IA64 - // IA64 (Itanium) will do it, if contractions are on. set_rounding_mode(rm); + +#ifdef _M_IA64 + // IA64 (Itanium) will do it, if contractions are on. o.value = x.value * y.value + z.value; #else - // NOT_IMPLEMENTED_YET(); - // Just a dummy for now: - hwf t; - mul(rm, x, y, t); - add(rm, t, z, o); +#if defined(_WINDOWS) +#if _MSC_VER >= 1800 + o.value = ::fma(x.value, y.value, z.value); +#else // Windows, older than VS 2013 + #ifdef USE_INTRINSICS + _mm_store_sd(&o.value, _mm_fmadd_sd(_mm_set_sd(x.value), _mm_set_sd(y.value), _mm_set_sd(z.value))); + #else + // If all else fails, we are imprecise. + o.value = (x.value * y.value) + z; + #endif +#endif +#else + // Linux, OSX + o.value = ::fma(x.value, y.value, z.value); +#endif #endif } @@ -338,8 +345,38 @@ void hwf_manager::sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o) { void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o) { set_rounding_mode(rm); - modf(x.value, &o.value); - // Note: on x64, this sometimes produces an SNAN instead of a QNAN? + // CMW: modf is not the right function here. + // modf(x.value, &o.value); + + // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the + // same in 32-bit and 64-bit mode. The _mm_round_* intrinsics are SSE4 extensions. +#ifdef _WINDOWS +#ifdef USE_INTRINSICS + switch (rm) { + case 0: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEAREST_INT)); break; + case 2: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_POS_INF)); break; + case 3: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_NEG_INF)); break; + case 4: _mm_store_sd(&o.value, _mm_round_pd(_mm_set_sd(x.value), _MM_FROUND_TO_ZERO)); break; + case 1: + UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! + break; + default: + UNREACHABLE(); // Unknown rounding mode. + } +#else + double xv = x.value; + double & ov = o.value; + + __asm { + fld xv + frndint + fstp ov // Store result away. + } +#endif +#else + // Linux, OSX. + o.value = nearbyint(x.value); +#endif } void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { diff --git a/src/util/hwf.h b/src/util/hwf.h index 9059869a0..97b4133f7 100644 --- a/src/util/hwf.h +++ b/src/util/hwf.h @@ -105,7 +105,7 @@ public: void mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); void div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o); - void fused_mul_add(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o); + void fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o); void sqrt(mpf_rounding_mode rm, hwf const & x, hwf & o); diff --git a/src/util/inf_eps_rational.h b/src/util/inf_eps_rational.h index 659fdc400..698075e01 100644 --- a/src/util/inf_eps_rational.h +++ b/src/util/inf_eps_rational.h @@ -127,6 +127,10 @@ class inf_eps_rational { return m_r.get_uint64(); } + Numeral const& get_numeral() const { + return m_r; + } + rational const& get_rational() const { return m_r.get_rational(); } @@ -139,16 +143,37 @@ class inf_eps_rational { return m_infty; } + bool is_finite() const { + return m_infty.is_zero(); + } + + static inf_eps_rational zero() { + return inf_eps_rational(Numeral::zero()); + } + + static inf_eps_rational one() { + return inf_eps_rational(Numeral::one()); + } + + static inf_eps_rational minus_one() { + return inf_eps_rational(Numeral::minus_one()); + } + + static inf_eps_rational infinity() { + return inf_eps_rational(rational::one(), Numeral::zero()); + } + + inf_eps_rational & operator=(const inf_eps_rational & r) { m_infty = r.m_infty; m_r = r.m_r; return *this; } - inf_eps_rational & operator=(const rational & r) { + inf_eps_rational & operator=(const Numeral & r) { m_infty.reset(); m_r = r; - return *this; + return *this; } inf_eps_rational & operator+=(const inf_eps_rational & r) { @@ -163,6 +188,16 @@ class inf_eps_rational { return *this; } + inf_eps_rational & operator-=(const inf_rational & r) { + m_r -= r; + return *this; + } + + inf_eps_rational & operator+=(const inf_rational & r) { + m_r += r; + return *this; + } + inf_eps_rational & operator+=(const rational & r) { m_r += r; return *this; @@ -272,12 +307,12 @@ class inf_eps_rational { } friend inline rational floor(const inf_eps_rational & r) { - SASSERT(r.m_infty.is_zero()); + // SASSERT(r.m_infty.is_zero()); return floor(r.m_r); } friend inline rational ceil(const inf_eps_rational & r) { - SASSERT(r.m_infty.is_zero()); + // SASSERT(r.m_infty.is_zero()); return ceil(r.m_r); } diff --git a/src/util/inf_int_rational.cpp b/src/util/inf_int_rational.cpp index 54a838a75..7bc68e641 100644 --- a/src/util/inf_int_rational.cpp +++ b/src/util/inf_int_rational.cpp @@ -19,9 +19,9 @@ Revision History: #include #include"inf_int_rational.h" -inf_int_rational inf_int_rational::m_zero(0); -inf_int_rational inf_int_rational::m_one(1); -inf_int_rational inf_int_rational::m_minus_one(-1); +inf_int_rational inf_int_rational::m_zero; +inf_int_rational inf_int_rational::m_one; +inf_int_rational inf_int_rational::m_minus_one; std::string inf_int_rational::to_string() const { if (m_second == 0) { @@ -39,3 +39,22 @@ std::string inf_int_rational::to_string() const { return s.str(); } +void initialize_inf_int_rational() { + inf_int_rational::init(); +} + +void inf_int_rational::init() { + m_zero.m_first = rational::zero(); + m_one.m_first = rational::one(); + m_minus_one.m_first = rational::minus_one(); +} + +void finalize_inf_int_rational() { + inf_int_rational::finalize(); +} + +void inf_int_rational::finalize() { + m_zero.~inf_int_rational(); + m_one.~inf_int_rational(); + m_minus_one.~inf_int_rational(); +} diff --git a/src/util/inf_int_rational.h b/src/util/inf_int_rational.h index c9879f0ee..06cbc1267 100644 --- a/src/util/inf_int_rational.h +++ b/src/util/inf_int_rational.h @@ -33,6 +33,8 @@ class inf_int_rational { rational m_first; int m_second; public: + static void init(); // called from rational::initialize() only + static void finalize(); // called from rational::finalize() only unsigned hash() const { return m_first.hash() ^ (static_cast(m_second) + 1); @@ -155,6 +157,17 @@ class inf_int_rational { return *this; } + inf_int_rational & operator*=(const rational & r) { + if (!r.is_int32()) { + throw default_exception("multiplication with large rational is not possible"); + } + m_first *= r; + m_second *= r.get_int32(); + return *this; + } + + + inf_int_rational & operator-=(const inf_int_rational & r) { m_first -= r.m_first; m_second -= r.m_second; @@ -261,7 +274,7 @@ class inf_int_rational { if (r.m_second >= 0) { return r.m_first; } - return r.m_first - rational(1); + return r.m_first - rational::one(); } return floor(r.m_first); @@ -272,7 +285,7 @@ class inf_int_rational { if (r.m_second <= 0) { return r.m_first; } - return r.m_first + rational(1); + return r.m_first + rational::one(); } return ceil(r.m_first); @@ -344,6 +357,10 @@ inline inf_int_rational operator+(const inf_int_rational & r1, const inf_int_rat return inf_int_rational(r1) += r2; } +inline inf_int_rational operator*(const rational & r1, const inf_int_rational & r2) { + return inf_int_rational(r2) *= r1; +} + inline inf_int_rational operator-(const inf_int_rational & r1, const inf_int_rational & r2) { return inf_int_rational(r1) -= r2; } diff --git a/src/util/inf_rational.cpp b/src/util/inf_rational.cpp index 3837d2c15..0b043e94d 100644 --- a/src/util/inf_rational.cpp +++ b/src/util/inf_rational.cpp @@ -18,9 +18,9 @@ Revision History: --*/ #include"inf_rational.h" -inf_rational inf_rational::m_zero(0); -inf_rational inf_rational::m_one(1); -inf_rational inf_rational::m_minus_one(-1); +inf_rational inf_rational::m_zero; +inf_rational inf_rational::m_one; +inf_rational inf_rational::m_minus_one; inf_rational inf_mult(inf_rational const& r1, inf_rational const& r2) { @@ -128,7 +128,7 @@ inf_rational inf_power(inf_rational const& r, unsigned n) // 0 will work. } else if (r.m_first.is_zero()) { - result.m_first = rational(-1); + result.m_first = rational::minus_one(); } else if (r.m_first.is_pos()) { result.m_first = rational(r.m_first - r.m_first/rational(2)).expt(n); @@ -152,7 +152,7 @@ inf_rational sup_power(inf_rational const& r, unsigned n) result.m_first = r.m_first.expt(n); } else if (r.m_first.is_zero() || (n == 0)) { - result.m_first = rational(1); + result.m_first = rational::one(); } else if (r.m_first.is_pos() || is_even) { result.m_first = rational(r.m_first + r.m_first/rational(2)).expt(n); @@ -177,3 +177,23 @@ inf_rational sup_root(inf_rational const& r, unsigned n) // use r. return r; } + +void initialize_inf_rational() { + inf_rational::init(); +} + +void inf_rational::init() { + m_zero.m_first = rational::zero(); + m_one.m_first = rational::one(); + m_minus_one.m_first = rational::minus_one(); +} + +void finalize_inf_rational() { + inf_rational::finalize(); +} + +void inf_rational::finalize() { + m_zero.~inf_rational(); + m_one.~inf_rational(); + m_minus_one.~inf_rational(); +} diff --git a/src/util/inf_rational.h b/src/util/inf_rational.h index 5cdfe9e93..b832b8793 100644 --- a/src/util/inf_rational.h +++ b/src/util/inf_rational.h @@ -33,6 +33,8 @@ class inf_rational { rational m_first; rational m_second; public: + static void init(); // called from rational::initialize() only + static void finalize(); // called from rational::finalize() only unsigned hash() const { return m_first.hash() ^ (m_second.hash()+1); @@ -40,7 +42,7 @@ class inf_rational { struct hash_proc { unsigned operator()(inf_rational const& r) const { return r.hash(); } }; - struct eq_proc { bool operator()(inf_rational const& r1, inf_rational const& r2) const { return r1 == r2; } }; + struct eq_proc { bool operator()(inf_rational const& r1, inf_rational const& r2) const { return r1 == r2; } }; void swap(inf_rational & n) { m_first.swap(n.m_first); @@ -63,10 +65,7 @@ class inf_rational { return s; } - inf_rational(): - m_first(rational()), - m_second(rational()) - {} + inf_rational() {} inf_rational(const inf_rational & r): m_first(r.m_first), @@ -85,10 +84,10 @@ class inf_rational { explicit inf_rational(rational const& r, bool pos_inf): m_first(r), - m_second(pos_inf?rational(1):rational(-1)) + m_second(pos_inf ? rational::one() : rational::minus_one()) {} - explicit inf_rational(rational const& r): + inf_rational(rational const& r): m_first(r) { m_second.reset(); @@ -316,7 +315,7 @@ class inf_rational { if (r.m_second.is_nonneg()) { return r.m_first; } - return r.m_first - rational(1); + return r.m_first - rational::one(); } return floor(r.m_first); @@ -327,7 +326,7 @@ class inf_rational { if (r.m_second.is_nonpos()) { return r.m_first; } - return r.m_first + rational(1); + return r.m_first + rational::one(); } return ceil(r.m_first); diff --git a/src/util/map.h b/src/util/map.h index d659b89c0..4640eb111 100644 --- a/src/util/map.h +++ b/src/util/map.h @@ -108,6 +108,10 @@ public: m_table.insert(key_data(k, v)); } + bool insert_if_not_there_core(key const & k, value const & v, entry *& et) { + return m_table.insert_if_not_there_core(key_data(k,v), et); + } + key_data const & insert_if_not_there(key const & k, value const & v) { return m_table.insert_if_not_there(key_data(k, v)); } diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index e911ff505..7539e7ba3 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -1,3 +1,9 @@ + +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + #include #include #include diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index c96ba594e..57e5b56b5 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -210,7 +210,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode std::string f, e; - f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; + f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); @@ -232,18 +232,18 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode o.ebits = ebits; o.sbits = sbits; - o.sign = m_mpq_manager.is_neg(significand); + o.sign = m_mpq_manager.is_neg(significand); if (m_mpq_manager.is_zero(significand)) mk_zero(ebits, sbits, o.sign, o); - else { + else { scoped_mpq sig(m_mpq_manager); scoped_mpz exp(m_mpq_manager); m_mpq_manager.set(sig, significand); m_mpq_manager.abs(sig); - m_mpz_manager.set(exp, exponent); - + m_mpz_manager.set(exp, exponent); + // Normalize while (m_mpq_manager.ge(sig, 2)) { m_mpq_manager.div(sig, mpq(2), sig); @@ -277,7 +277,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode TRACE("mpf_dbg", tout << "sig = " << m_mpz_manager.to_string(o.significand) << " exp = " << o.exponent << std::endl;); - + if (m_mpz_manager.is_small(exp)) { o.exponent = m_mpz_manager.get_int64(exp); round(rm, o); @@ -348,7 +348,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode bool sticky = false; while (ds < 0) { - if (!m_mpz_manager.is_even(o.significand)) sticky = true; + sticky |= m_mpz_manager.is_odd(o.significand); m_mpz_manager.machine_div2k(o.significand, 1); ds++; } @@ -1003,9 +1003,38 @@ void mpf_manager::round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o mk_nan(x.ebits, x.sbits, o); else if (is_inf(x)) set(o, x); - else if (x.exponent < 0) - mk_zero(x.ebits, x.sbits, x.sign, o); - else if (x.exponent >= x.sbits-1) + else if (is_zero(x)) + mk_zero(x.ebits, x.sbits, x.sign, o); // -0.0 -> -0.0, says IEEE754, Sec 5.9. + else if (x.exponent < 0) { + if (rm == MPF_ROUND_TOWARD_ZERO) + mk_zero(x.ebits, x.sbits, x.sign, o); + else if (rm == MPF_ROUND_TOWARD_NEGATIVE) { + if (x.sign) + mk_one(x.ebits, x.sbits, true, o); + else + mk_zero(x.ebits, x.sbits, false, o); + } + else if (rm == MPF_ROUND_TOWARD_POSITIVE) { + if (x.sign) + mk_zero(x.ebits, x.sbits, true, o); + else + mk_one(x.ebits, x.sbits, false, o); + } + else { + SASSERT(rm == MPF_ROUND_NEAREST_TEVEN || rm == MPF_ROUND_NEAREST_TAWAY); + bool tie = m_mpz_manager.is_zero(x.significand) && x.exponent == -1; + TRACE("mpf_dbg", tout << "tie = " << tie << std::endl;); + if (tie && rm == MPF_ROUND_NEAREST_TEVEN) + mk_zero(x.ebits, x.sbits, x.sign, o); + else if (tie && rm == MPF_ROUND_NEAREST_TAWAY) + mk_one(x.ebits, x.sbits, x.sign, o); + else if (x.exponent < -1) + mk_zero(x.ebits, x.sbits, x.sign, o); + else + mk_one(x.ebits, x.sbits, x.sign, o); + } + } + else if (x.exponent >= x.sbits - 1) set(o, x); else { SASSERT(x.exponent >= 0 && x.exponent < x.sbits-1); @@ -1016,21 +1045,74 @@ void mpf_manager::round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o scoped_mpf a(*this); set(a, x); - unpack(a, true); + unpack(a, true); // A includes hidden bit + + TRACE("mpf_dbg", tout << "A = " << to_string_raw(a) << std::endl;); + + SASSERT(m_mpz_manager.lt(a.significand(), m_powers2(x.sbits))); + SASSERT(m_mpz_manager.ge(a.significand(), m_powers2(x.sbits - 1))); - TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); - o.exponent = a.exponent(); m_mpz_manager.set(o.significand, a.significand()); + + unsigned shift = (o.sbits - 1) - ((unsigned)o.exponent); + const mpz & shift_p = m_powers2(shift); + TRACE("mpf_dbg", tout << "shift=" << shift << std::endl;); + + scoped_mpz div(m_mpz_manager), rem(m_mpz_manager); + m_mpz_manager.machine_div_rem(o.significand, shift_p, div, rem); + TRACE("mpf_dbg", tout << "div=" << m_mpz_manager.to_string(div) << " rem=" << m_mpz_manager.to_string(rem) << std::endl;); - unsigned q = (unsigned) o.exponent; - unsigned shift = o.sbits-q-1; - TRACE("mpf_dbg", tout << "Q = " << q << " shift=" << shift << std::endl;); - m_mpz_manager.machine_div2k(o.significand, shift); - m_mpz_manager.mul2k(o.significand, shift+3); + const mpz & shift_p1 = m_powers2(shift-1); + TRACE("mpf_dbg", tout << "shift_p1=" << m_mpz_manager.to_string(shift_p1) << std::endl;); - round(rm, o); - } + switch (rm) { + case MPF_ROUND_NEAREST_TEVEN: + case MPF_ROUND_NEAREST_TAWAY: { + bool tie = m_mpz_manager.eq(rem, shift_p1); + bool less_than_tie = m_mpz_manager.lt(rem, shift_p1); + bool more_than_tie = m_mpz_manager.gt(rem, shift_p1); + TRACE("mpf_dbg", tout << "tie= " << tie << "; tie = " << more_than_tie << std::endl;); + if (tie) { + if ((rm == MPF_ROUND_NEAREST_TEVEN && m_mpz_manager.is_odd(div)) || + (rm == MPF_ROUND_NEAREST_TAWAY && m_mpz_manager.is_even(div))) { + TRACE("mpf_dbg", tout << "div++ (1)" << std::endl;); + m_mpz_manager.inc(div); + } + } + else { + SASSERT(less_than_tie || more_than_tie); + if (more_than_tie) { + m_mpz_manager.inc(div); + TRACE("mpf_dbg", tout << "div++ (2)" << std::endl;); + } + } + break; + } + case MPF_ROUND_TOWARD_POSITIVE: + if (!m_mpz_manager.is_zero(rem) && !o.sign) + m_mpz_manager.inc(div); + break; + case MPF_ROUND_TOWARD_NEGATIVE: + if (!m_mpz_manager.is_zero(rem) && o.sign) + m_mpz_manager.inc(div); + break; + case MPF_ROUND_TOWARD_ZERO: + default: + /* nothing */; + } + + m_mpz_manager.mul2k(div, shift, o.significand); + SASSERT(m_mpz_manager.ge(o.significand, m_powers2(o.sbits - 1))); + + // re-normalize + while (m_mpz_manager.ge(o.significand, m_powers2(o.sbits))) { + m_mpz_manager.machine_div2k(o.significand, 1); + o.exponent++; + } + + m_mpz_manager.sub(o.significand, m_powers2(o.sbits - 1), o.significand); // strip hidden bit + } TRACE("mpf_dbg", tout << "INTEGRAL = " << to_string(o) << std::endl;); } @@ -1099,10 +1181,10 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { mk_nan(x.ebits, x.sbits, o); else if (is_inf(y)) set(o, x); - else if (is_zero(x)) - set(o, x); else if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); + else if (is_zero(x)) + set(o, x); else { o.ebits = x.ebits; o.sbits = x.sbits; @@ -1147,7 +1229,11 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { } void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { - if (is_nan(x) || (is_zero(x) && is_zero(y))) + if (is_nan(x)) + set(o, y); + else if (is_zero(x) && is_zero(y) && sgn(x) != sgn(y)) + mk_pzero(x.ebits, x.sbits, o); + else if (is_zero(x) && is_zero(y)) set(o, y); else if (is_nan(y)) set(o, x); @@ -1158,7 +1244,11 @@ void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { } void mpf_manager::minimum(mpf const & x, mpf const & y, mpf & o) { - if (is_nan(x) || (is_zero(x) && is_zero(y))) + if (is_nan(x)) + set(o, y); + else if (is_zero(x) && is_zero(y) && sgn(x) != sgn(y)) + mk_pzero(x.ebits, x.sbits, o); + else if (is_zero(x) && is_zero(y)) set(o, y); else if (is_nan(y)) set(o, x); @@ -1350,7 +1440,7 @@ bool mpf_manager::is_ninf(mpf const & x) { } bool mpf_manager::is_normal(mpf const & x) { - return !(has_top_exp(x) || is_denormal(x)); + return !(has_top_exp(x) || is_denormal(x) || is_zero(x)); } bool mpf_manager::is_denormal(mpf const & x) { @@ -1449,6 +1539,14 @@ void mpf_manager::mk_nan(unsigned ebits, unsigned sbits, mpf & o) { o.sign = false; } +void mpf_manager::mk_one(unsigned ebits, unsigned sbits, bool sign, mpf & o) const { + o.sbits = sbits; + o.ebits = ebits; + o.sign = sign; + m_mpz_manager.set(o.significand, 0); + o.exponent = 0; +} + void mpf_manager::mk_max_value(unsigned ebits, unsigned sbits, bool sign, mpf & o) { o.sbits = sbits; o.ebits = ebits; @@ -1475,6 +1573,9 @@ void mpf_manager::mk_ninf(unsigned ebits, unsigned sbits, mpf & o) { void mpf_manager::unpack(mpf & o, bool normalize) { // Insert the hidden bit or adjust the exponent of denormal numbers. + if (is_zero(o)) + return; + if (is_normal(o)) m_mpz_manager.add(o.significand, m_powers2(o.sbits-1), o.significand); else { diff --git a/src/util/mpf.h b/src/util/mpf.h index ecacb3301..28329810e 100644 --- a/src/util/mpf.h +++ b/src/util/mpf.h @@ -46,7 +46,7 @@ class mpf { mpz significand; mpf_exp_t exponent; mpf & operator=(mpf const & other) { UNREACHABLE(); return *this; } - void set(unsigned ebits, unsigned sbits); + void set(unsigned _ebits, unsigned _sbits); public: mpf(); mpf(unsigned ebits, unsigned sbits); @@ -208,7 +208,9 @@ public: void to_sbv_mpq(mpf_rounding_mode rm, const mpf & x, scoped_mpq & o); -protected: +protected: + void mk_one(unsigned ebits, unsigned sbits, bool sign, mpf & o) const; + bool has_bot_exp(mpf const & x); bool has_top_exp(mpf const & x); diff --git a/src/util/mpff.cpp b/src/util/mpff.cpp index b979b32ff..e5d3cf24c 100644 --- a/src/util/mpff.cpp +++ b/src/util/mpff.cpp @@ -255,7 +255,7 @@ void mpff_manager::set(mpff & n, int64 v) { } else { if (v < 0) { - set(n, static_cast(-v)); + set(n, 1 + static_cast(-(1+v))); n.m_sign = 1; } else { @@ -680,7 +680,7 @@ void mpff_manager::add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c // Make sure that a and b have the same exponent. if (exp_a > exp_b) { - unsigned shift = exp_a - exp_b; + unsigned shift = (unsigned)exp_a - (unsigned)exp_b; n_sig_b = m_buffers[0].c_ptr(); shr(m_precision, sig_b, shift, m_precision, n_sig_b); if (sgn_b != m_to_plus_inf && has_one_at_first_k_bits(m_precision, sig_b, shift)) { diff --git a/src/util/mpn.h b/src/util/mpn.h index 495ae135c..c5566bf7e 100644 --- a/src/util/mpn.h +++ b/src/util/mpn.h @@ -27,7 +27,9 @@ Revision History: typedef unsigned int mpn_digit; class mpn_manager { +#ifndef _NO_OMP_ omp_nest_lock_t m_lock; +#endif #define MPN_BEGIN_CRITICAL() omp_set_nest_lock(&m_lock); #define MPN_END_CRITICAL() omp_unset_nest_lock(&m_lock); diff --git a/src/util/mpq_inf.cpp b/src/util/mpq_inf.cpp index 6f6d805f2..19289b737 100644 --- a/src/util/mpq_inf.cpp +++ b/src/util/mpq_inf.cpp @@ -19,7 +19,7 @@ Revision History: #include"mpq_inf.h" template -std::string mpq_inf_manager::to_string(mpq_inf const & a) const { +std::string mpq_inf_manager::to_string(mpq_inf const & a) { if (m.is_zero(a.second)) return m.to_string(a.first); @@ -38,5 +38,6 @@ std::string mpq_inf_manager::to_string(mpq_inf const & a) const { return s; } + template class mpq_inf_manager; template class mpq_inf_manager; diff --git a/src/util/mpq_inf.h b/src/util/mpq_inf.h index 217b58567..640c827ae 100644 --- a/src/util/mpq_inf.h +++ b/src/util/mpq_inf.h @@ -26,12 +26,12 @@ typedef std::pair mpq_inf; template class mpq_inf_manager { - mpq_manager & m; + mpq_manager m; double m_inf; public: typedef mpq_inf numeral; - mpq_inf_manager(mpq_manager & _m, double inf = 0.0001):m(_m) { + mpq_inf_manager(double inf = 0.0001) { set_inf(inf); } @@ -83,6 +83,14 @@ public: } bool is_int(mpq_inf const & a) const { return m.is_int(a.first) && m.is_zero(a.second); } + + bool is_pos(mpq_inf const & a) const { + return m.is_pos(a.first) || (m.is_zero(a.first) && m.is_pos(a.second)); + } + + bool is_neg(mpq_inf const & a) const { + return m.is_neg(a.first) || (m.is_zero(a.first) && m.is_neg(a.second)); + } bool is_rational(mpq_inf const & a) const { return m.is_zero(a.second); } @@ -104,15 +112,15 @@ public: return m.is_zero(a.first) && m.is_zero(a.second); } - bool eq(mpq_inf const & a, mpq_inf const & b) const { + bool eq(mpq_inf const & a, mpq_inf const & b) { return m.eq(a.first, b.first) && m.eq(a.second, b.second); } - bool eq(mpq_inf const & a, mpq const & b) const { + bool eq(mpq_inf const & a, mpq const & b) { return m.eq(a.first, b) && m.is_zero(a.second); } - bool eq(mpq_inf const & a, mpq const & b, inf_kind k) const { + bool eq(mpq_inf const & a, mpq const & b, inf_kind k) { if (!m.eq(a.first, b)) return false; switch (k) { @@ -124,15 +132,15 @@ public: return false; } - bool lt(mpq_inf const & a, mpq_inf const & b) const { + bool lt(mpq_inf const & a, mpq_inf const & b) { return m.lt(a.first, b.first) || (m.lt(a.second, b.second) && m.eq(a.first, b.first)); } - bool lt(mpq_inf const & a, mpq const & b) const { + bool lt(mpq_inf const & a, mpq const & b) { return m.lt(a.first, b) || (m.is_neg(a.second) && m.eq(a.first, b)); } - bool lt(mpq_inf const & a, mpq const & b, inf_kind k) const { + bool lt(mpq_inf const & a, mpq const & b, inf_kind k) { if (m.lt(a.first, b)) return true; if (m.eq(a.first, b)) { @@ -146,13 +154,13 @@ public: return false; } - bool gt(mpq_inf const & a, mpq_inf const & b) const { return lt(b, a); } + bool gt(mpq_inf const & a, mpq_inf const & b) { return lt(b, a); } - bool gt(mpq_inf const & a, mpq const & b) const { + bool gt(mpq_inf const & a, mpq const & b) { return m.gt(a.first, b) || (m.is_pos(a.second) && m.eq(a.first, b)); } - bool gt(mpq_inf const & a, mpq const & b, inf_kind k) const { + bool gt(mpq_inf const & a, mpq const & b, inf_kind k) { if (m.gt(a.first, b)) return true; if (m.eq(a.first, b)) { @@ -166,17 +174,17 @@ public: return false; } - bool le(mpq_inf const & a, mpq_inf const & b) const { return !gt(a, b); } + bool le(mpq_inf const & a, mpq_inf const & b) { return !gt(a, b); } - bool le(mpq_inf const & a, mpq const & b) const { return !gt(a, b); } + bool le(mpq_inf const & a, mpq const & b) { return !gt(a, b); } - bool le(mpq_inf const & a, mpq const & b, inf_kind k) const { return !gt(a, b, k); } + bool le(mpq_inf const & a, mpq const & b, inf_kind k) { return !gt(a, b, k); } - bool ge(mpq_inf const & a, mpq_inf const & b) const { return !lt(a, b); } + bool ge(mpq_inf const & a, mpq_inf const & b) { return !lt(a, b); } - bool ge(mpq_inf const & a, mpq const & b) const { return !lt(a, b); } + bool ge(mpq_inf const & a, mpq const & b) { return !lt(a, b); } - bool ge(mpq_inf const & a, mpq const & b, inf_kind k) const { return !lt(a, b, k); } + bool ge(mpq_inf const & a, mpq const & b, inf_kind k) { return !lt(a, b, k); } void add(mpq_inf const & a, mpq_inf const & b, mpq_inf & c) { m.add(a.first, b.first, c.first); @@ -208,6 +216,16 @@ public: m.mul(b, a.second, c.second); } + void div(mpq_inf const & a, mpq const & b, mpq_inf & c) { + m.div(a.first, b, c.first); + m.div(a.second, b, c.second); + } + + void div(mpq_inf const & a, mpz const & b, mpq_inf & c) { + m.div(a.first, b, c.first); + m.div(a.second, b, c.second); + } + void inc(mpq_inf & a) { m.inc(a.first); } @@ -221,10 +239,16 @@ public: m.neg(a.second); } + void abs(mpq_inf & a) { + if (is_neg(a)) { + neg(a); + } + } + void ceil(mpq_inf const & a, mpq & b) { if (m.is_int(a.first)) { // special cases for k - delta*epsilon where k is an integer - if (m.is_pos(a.first)) + if (m.is_pos(a.second)) m.add(a.first, mpq(1), b); // ceil(k + delta*epsilon) --> k+1 else m.set(b, a.first); @@ -246,7 +270,13 @@ public: } } - std::string to_string(mpq_inf const & a) const; + std::string to_string(mpq_inf const & a); + + void display(std::ostream & out, mpq_inf const & a) { + out << to_string(a); + } + + mpq_manager& get_mpq_manager() { return m; } }; typedef mpq_inf_manager synch_mpq_inf_manager; diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 163f7fb86..855927919 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -336,17 +336,17 @@ void mpz_manager::set(mpz & target, unsigned sz, digit_t const * digits) } } #else - mk_big(target); - // reset - mpz_set_ui(*target.m_ptr, digits[sz - 1]); - SASSERT(sz > 0); - unsigned i = sz - 1; - while (i > 0) { - --i; - mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); - mpz_set_ui(m_tmp, digits[i]); - mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); - } + mk_big(target); + // reset + mpz_set_ui(*target.m_ptr, digits[sz - 1]); + SASSERT(sz > 0); + unsigned i = sz - 1; + while (i > 0) { + --i; + mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); + mpz_set_ui(m_tmp, digits[i]); + mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); + } #endif } } @@ -2037,16 +2037,16 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { } return a.m_val < 0; #else - bool r = is_neg(a); - mpz_set(m_tmp, *a.m_ptr); - mpz_abs(m_tmp, m_tmp); - while (mpz_sgn(m_tmp) != 0) { - mpz_tdiv_r_2exp(m_tmp2, m_tmp, 32); - unsigned v = mpz_get_ui(m_tmp2); - digits.push_back(v); - mpz_tdiv_q_2exp(m_tmp, m_tmp, 32); - } - return r; + bool r = is_neg(a); + mpz_set(m_tmp, *a.m_ptr); + mpz_abs(m_tmp, m_tmp); + while (mpz_sgn(m_tmp) != 0) { + mpz_tdiv_r_2exp(m_tmp2, m_tmp, 32); + unsigned v = mpz_get_ui(m_tmp2); + digits.push_back(v); + mpz_tdiv_q_2exp(m_tmp, m_tmp, 32); + } + return r; #endif } } diff --git a/src/util/rational.cpp b/src/util/rational.cpp index 122db7217..43dc9110a 100644 --- a/src/util/rational.cpp +++ b/src/util/rational.cpp @@ -24,14 +24,14 @@ Revision History: #endif synch_mpq_manager * rational::g_mpq_manager = 0; -rational rational::m_zero(0); -rational rational::m_one(1); -rational rational::m_minus_one(-1); +rational rational::m_zero; +rational rational::m_one; +rational rational::m_minus_one; vector rational::m_powers_of_two; -void mk_power_up_to(vector & pws, unsigned n) { +static void mk_power_up_to(vector & pws, unsigned n) { if (pws.empty()) { - pws.push_back(rational(1)); + pws.push_back(rational::one()); } unsigned sz = pws.size(); rational curr = pws[sz - 1]; @@ -53,14 +53,32 @@ rational rational::power_of_two(unsigned k) { return result; } +// in inf_rational.cpp +void initialize_inf_rational(); +void finalize_inf_rational(); + +// in inf_int_rational.cpp +void initialize_inf_int_rational(); +void finalize_inf_int_rational(); + void rational::initialize() { if (!g_mpq_manager) { g_mpq_manager = alloc(synch_mpq_manager); + m().set(m_zero.m_val, 0); + m().set(m_one.m_val, 1); + m().set(m_minus_one.m_val, -1); + initialize_inf_rational(); + initialize_inf_int_rational(); } } void rational::finalize() { + finalize_inf_rational(); + finalize_inf_int_rational(); m_powers_of_two.finalize(); + m_zero.~rational(); + m_one.~rational(); + m_minus_one.~rational(); dealloc(g_mpq_manager); g_mpq_manager = 0; } diff --git a/src/util/rational.h b/src/util/rational.h index 0d8a23e81..379f4868c 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -111,6 +111,11 @@ public: return INT_MIN <= v && v <= INT_MAX; } + int get_int32() const { + SASSERT(is_int32()); + return (int)get_int64(); + } + double get_double() const { return m().get_double(m_val); } rational const & get_rational() const { return *this; } diff --git a/src/util/region.h b/src/util/region.h index 7f6bc787e..5be2ae4d3 100644 --- a/src/util/region.h +++ b/src/util/region.h @@ -53,6 +53,7 @@ public: m_scopes.push_back(m_chuncks.size()); } + void pop_scope() { unsigned old_size = m_scopes.back(); m_scopes.pop_back(); diff --git a/src/util/s_integer.h b/src/util/s_integer.h index 4e50269c5..92321a7c3 100644 --- a/src/util/s_integer.h +++ b/src/util/s_integer.h @@ -67,7 +67,7 @@ public: s_integer const& get_s_integer() const { return *this; } s_integer const& get_infinitesimal() const { return zero(); } static bool is_rational() { return true; } - s_integer const& get_rational() const { return *this; } + s_integer const& get_rational() const { return *this; } s_integer & operator=(const s_integer & r) { m_val = r.m_val; return *this; } friend inline s_integer numerator(const s_integer & r) { return r; } friend inline s_integer denominator(const s_integer & r) { return one(); } diff --git a/src/util/scoped_numeral.h b/src/util/scoped_numeral.h index 25e6422df..0023be7e2 100644 --- a/src/util/scoped_numeral.h +++ b/src/util/scoped_numeral.h @@ -39,7 +39,7 @@ public: numeral const & get() const { return m_num; } numeral & get() { return m_num; } - _scoped_numeral & operator=(_scoped_numeral & n) { + _scoped_numeral & operator=(_scoped_numeral const & n) { if (this == &n) return *this; m().set(m_num, n.m_num); @@ -97,6 +97,10 @@ public: return a.m().eq(a, b); } + friend bool operator!=(_scoped_numeral const & a, numeral const & b) { + return !a.m().eq(a, b); + } + friend bool operator<(_scoped_numeral const & a, numeral const & b) { return a.m().lt(a, b); } @@ -113,6 +117,26 @@ public: return a.m().ge(a, b); } + bool is_zero() const { + return m().is_zero(*this); + } + + bool is_pos() const { + return m().is_pos(*this); + } + + bool is_neg() const { + return m().is_neg(*this); + } + + bool is_nonpos() const { + return m().is_nonpos(*this); + } + + bool is_nonneg() const { + return m().is_nonneg(*this); + } + friend bool is_zero(_scoped_numeral const & a) { return a.m().is_zero(a); } @@ -133,6 +157,12 @@ public: return a.m().is_nonpos(a); } + friend _scoped_numeral abs(_scoped_numeral const& a) { + _scoped_numeral res(a); + a.m().abs(res); + return res; + } + void neg() { m().neg(m_num); } diff --git a/src/util/scoped_numeral_vector.h b/src/util/scoped_numeral_vector.h index 4375babb3..f982a7cae 100644 --- a/src/util/scoped_numeral_vector.h +++ b/src/util/scoped_numeral_vector.h @@ -46,6 +46,10 @@ public: m_manager.set(this->back(), v); } + void pop_back() { + shrink(this->size()-1); + } + void shrink(unsigned sz) { unsigned old_sz = this->size(); if (old_sz == sz) diff --git a/src/util/sorting_network.h b/src/util/sorting_network.h new file mode 100644 index 000000000..ee01d2cd2 --- /dev/null +++ b/src/util/sorting_network.h @@ -0,0 +1,767 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + sorting_network.h + +Abstract: + + Utility for creating a sorting network. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-11-07 + +Notes: + + Same routine is used in Formula. + +--*/ + +#include "vector.h" + +#ifndef _SORTING_NETWORK_H_ +#define _SORTING_NETWORK_H_ + + + template + class sorting_network { + typedef typename Ext::vector vect; + Ext& m_ext; + svector m_currentv; + svector m_nextv; + svector* m_current; + svector* m_next; + + unsigned& current(unsigned i) { return (*m_current)[i]; } + unsigned& next(unsigned i) { return (*m_next)[i]; } + + void exchange(unsigned i, unsigned j, vect& out) { + SASSERT(i <= j); + if (i < j) { + typename Ext::T ei = out.get(i); + typename Ext::T ej = out.get(j); + out.set(i, m_ext.mk_ite(m_ext.mk_le(ei, ej), ei, ej)); + out.set(j, m_ext.mk_ite(m_ext.mk_le(ej, ei), ei, ej)); + } + } + + void sort(unsigned k, vect& out) { + SASSERT(is_power_of2(k) && k > 0); + if (k == 2) { + for (unsigned i = 0; i < out.size()/2; ++i) { + exchange(current(2*i), current(2*i+1), out); + next(2*i) = current(2*i); + next(2*i+1) = current(2*i+1); + } + std::swap(m_current, m_next); + } + else { + + for (unsigned i = 0; i < out.size()/k; ++i) { + unsigned ki = k * i; + for (unsigned j = 0; j < k / 2; ++j) { + next(ki + j) = current(ki + (2 * j)); + next(ki + (k / 2) + j) = current(ki + (2 * j) + 1); + } + } + + std::swap(m_current, m_next); + sort(k / 2, out); + for (unsigned i = 0; i < out.size() / k; ++i) { + unsigned ki = k * i; + for (unsigned j = 0; j < k / 2; ++j) { + next(ki + (2 * j)) = current(ki + j); + next(ki + (2 * j) + 1) = current(ki + (k / 2) + j); + } + + for (unsigned j = 0; j < (k / 2) - 1; ++j) { + exchange(next(ki + (2 * j) + 1), next(ki + (2 * (j + 1))), out); + } + } + std::swap(m_current, m_next); + } + } + + bool is_power_of2(unsigned n) const { + return n != 0 && ((n-1) & n) == 0; + } + + public: + sorting_network(Ext& ext): + m_ext(ext), + m_current(&m_currentv), + m_next(&m_nextv) + {} + + void operator()(vect const& in, vect& out) { + out.reset(); + out.append(in); + if (in.size() <= 1) { + return; + } + while (!is_power_of2(out.size())) { + out.push_back(m_ext.mk_default()); + } + for (unsigned i = 0; i < out.size(); ++i) { + m_currentv.push_back(i); + m_nextv.push_back(i); + } + unsigned k = 2; + while (k <= out.size()) { + sort(k, out); + k *= 2; + } + } + }; + + // parametric sorting network + // Described in Abio et.al. CP 2013. + template + class psort_nw { + typedef typename psort_expr::literal literal; + typedef typename psort_expr::literal_vector literal_vector; + + class vc { + unsigned v; // number of vertices + unsigned c; // number of clauses + static const unsigned lambda = 5; + public: + vc(unsigned v, unsigned c):v(v), c(c) {} + + bool operator<(vc const& other) const { + return to_int() < other.to_int(); + } + vc operator+(vc const& other) const { + return vc(v + other.v, c + other.c); + } + unsigned to_int() const { + return lambda*v + c; + } + vc operator*(unsigned n) const { + return vc(n*v, n*c); + } + }; + + static vc mk_min(vc const& v1, vc const& v2) { + return (v1.to_int() < v2.to_int())?v1:v2; + } + + + enum cmp_t { LE, GE, EQ, GE_FULL, LE_FULL }; + psort_expr& ctx; + cmp_t m_t; + + // for testing + static const bool m_disable_dcard = false; + static const bool m_disable_dsorting = false; + static const bool m_disable_dsmerge = false; + static const bool m_force_dcard = false; + static const bool m_force_dsorting = false; + static const bool m_force_dsmerge = false; + + public: + struct stats { + unsigned m_num_compiled_vars; + unsigned m_num_compiled_clauses; + void reset() { memset(this, 0, sizeof(*this)); } + stats() { reset(); } + }; + stats m_stats; + + psort_nw(psort_expr& c): ctx(c) {} + + literal ge(bool full, unsigned k, unsigned n, literal const* xs) { + if (k > n) { + return ctx.mk_false(); + } + if (k == 0) { + return ctx.mk_true(); + } + SASSERT(0 < k && k <= n); + literal_vector in, out; + if (dualize(k, n, xs, in)) { + return le(full, k, in.size(), in.c_ptr()); + } + else { + SASSERT(2*k <= n); + m_t = full?GE_FULL:GE; + psort_nw::card(k, n, xs, out); + return out[k-1]; + } + } + + literal le(bool full, unsigned k, unsigned n, literal const* xs) { + if (k >= n) { + return ctx.mk_true(); + } + SASSERT(k < n); + literal_vector in, out; + if (dualize(k, n, xs, in)) { + return ge(full, k, n, in.c_ptr()); + } + else { + SASSERT(2*k <= n); + m_t = full?LE_FULL:LE; + card(k + 1, n, xs, out); + return ctx.mk_not(out[k]); + } + } + + literal eq(unsigned k, unsigned n, literal const* xs) { + if (k > n) { + return ctx.mk_false(); + } + SASSERT(k <= n); + literal_vector in, out; + if (dualize(k, n, xs, in)) { + return eq(k, n, in.c_ptr()); + } + else { + SASSERT(2*k <= n); + m_t = EQ; + card(k+1, n, xs, out); + SASSERT(out.size() >= k+1); + return ctx.mk_min(out[k-1], ctx.mk_not(out[k])); + } + } + + + private: + + std::ostream& pp(std::ostream& out, unsigned n, literal const* lits) { + for (unsigned i = 0; i < n; ++i) ctx.pp(out, lits[i]) << " "; + return out; + } + + std::ostream& pp(std::ostream& out, literal_vector const& lits) { + for (unsigned i = 0; i < lits.size(); ++i) ctx.pp(out, lits[i]) << " "; + return out; + } + + // 0 <= k <= N + // SUM x_i >= k + // <=> + // SUM ~x_i <= N - k + // suppose k > N/2, then it is better to solve dual. + + bool dualize(unsigned& k, unsigned N, literal const* xs, literal_vector& in) { + SASSERT(0 <= k && k <= N); + if (2*k <= N) { + return false; + } + k = N - k; + for (unsigned i = 0; i < N; ++i) { + in.push_back(ctx.mk_not(xs[i])); + } + TRACE("pb", + pp(tout << N << ": ", in); + tout << " ~ " << k << "\n";); + return true; + } + + + bool even(unsigned n) const { return (0 == (n & 0x1)); } + bool odd(unsigned n) const { return !even(n); } + unsigned ceil2(unsigned n) const { return n/2 + odd(n); } + unsigned floor2(unsigned n) const { return n/2; } + unsigned power2(unsigned n) const { SASSERT(n < 10); return 1 << n; } + + + literal mk_max(literal a, literal b) { + if (a == b) return a; + m_stats.m_num_compiled_vars++; + return ctx.mk_max(a, b); + } + + literal mk_min(literal a, literal b) { + if (a == b) return a; + m_stats.m_num_compiled_vars++; + return ctx.mk_min(a, b); + } + + literal fresh() { + m_stats.m_num_compiled_vars++; + return ctx.fresh(); + } + void add_clause(literal l1, literal l2, literal l3) { + literal lits[3] = { l1, l2, l3 }; + add_clause(3, lits); + } + void add_clause(literal l1, literal l2) { + literal lits[2] = { l1, l2 }; + add_clause(2, lits); + } + void add_clause(unsigned n, literal const* ls) { + m_stats.m_num_compiled_clauses++; + literal_vector tmp(n, ls); + ctx.mk_clause(n, tmp.c_ptr()); + } + + // y1 <= mk_max(x1,x2) + // y2 <= mk_min(x1,x2) + void cmp_ge(literal x1, literal x2, literal y1, literal y2) { + add_clause(ctx.mk_not(y2), x1); + add_clause(ctx.mk_not(y2), x2); + add_clause(ctx.mk_not(y1), x1, x2); + } + + // mk_max(x1,x2) <= y1 + // mk_min(x1,x2) <= y2 + void cmp_le(literal x1, literal x2, literal y1, literal y2) { + add_clause(ctx.mk_not(x1), y1); + add_clause(ctx.mk_not(x2), y1); + add_clause(ctx.mk_not(x1), ctx.mk_not(x2), y2); + } + + void cmp_eq(literal x1, literal x2, literal y1, literal y2) { + cmp_ge(x1, x2, y1, y2); + cmp_le(x1, x2, y1, y2); + } + + void cmp(literal x1, literal x2, literal y1, literal y2) { + switch(m_t) { + case LE: case LE_FULL: cmp_le(x1, x2, y1, y2); break; + case GE: case GE_FULL: cmp_ge(x1, x2, y1, y2); break; + case EQ: cmp_eq(x1, x2, y1, y2); break; + } + } + vc vc_cmp() { + return vc(2, (m_t==EQ)?6:3); + } + + void card(unsigned k, unsigned n, literal const* xs, literal_vector& out) { + TRACE("pb", tout << "card k:" << k << " n: " << n << "\n";); + if (n <= k) { + psort_nw::sorting(n, xs, out); + } + else if (use_dcard(k, n)) { + dsorting(k, n, xs, out); + } + else { + literal_vector out1, out2; + unsigned l = n/2; // TBD + card(k, l, xs, out1); + card(k, n-l, xs + l, out2); + smerge(k, out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); + } + TRACE("pb", tout << "card k:" << k << " n: " << n << "\n"; + pp(tout << "in:", n, xs) << "\n"; + pp(tout << "out:", out) << "\n";); + + } + vc vc_card(unsigned k, unsigned n) { + if (n <= k) { + return vc_sorting(n); + } + else if (use_dcard(k, n)) { + return vc_dsorting(k, n); + } + else { + return vc_card_rec(k, n); + } + } + vc vc_card_rec(unsigned k, unsigned n) { + unsigned l = n/2; + return vc_card(k, l) + vc_card(k, n-l) + vc_smerge(k, l, n-l); + } + bool use_dcard(unsigned k, unsigned n) { + return m_force_dcard || (!m_disable_dcard && n < 10 && vc_dsorting(k, n) < vc_card_rec(k, n)); + } + + + void merge(unsigned a, literal const* as, + unsigned b, literal const* bs, + literal_vector& out) { + TRACE("pb", tout << "merge a: " << a << " b: " << b << "\n";); + if (a == 1 && b == 1) { + literal y1 = mk_max(as[0], bs[0]); + literal y2 = mk_min(as[0], bs[0]); + out.push_back(y1); + out.push_back(y2); + psort_nw::cmp(as[0], bs[0], y1, y2); + } + else if (a == 0) { + out.append(b, bs); + } + else if (b == 0) { + out.append(a, as); + } + else if (use_dsmerge(a, b, a + b)) { + dsmerge(a + b, a, as, b, bs, out); + } + else if (even(a) && odd(b)) { + merge(b, bs, a, as, out); + } + else { + literal_vector even_a, odd_a; + literal_vector even_b, odd_b; + literal_vector out1, out2; + SASSERT(a > 1 || b > 1); + split(a, as, even_a, odd_a); + split(b, bs, even_b, odd_b); + SASSERT(!even_a.empty()); + SASSERT(!even_b.empty()); + merge(even_a.size(), even_a.c_ptr(), + even_b.size(), even_b.c_ptr(), out1); + merge(odd_a.size(), odd_a.c_ptr(), + odd_b.size(), odd_b.c_ptr(), out2); + interleave(out1, out2, out); + } + TRACE("pb", tout << "merge a: " << a << " b: " << b << "\n"; + pp(tout << "a:", a, as) << "\n"; + pp(tout << "b:", b, bs) << "\n"; + pp(tout << "out:", out) << "\n";); + } + vc vc_merge(unsigned a, unsigned b) { + if (a == 1 && b == 1) { + return vc_cmp(); + } + else if (a == 0 || b == 0) { + return vc(0, 0); + } + else if (use_dsmerge(a, b, a + b)) { + return vc_dsmerge(a, b, a + b); + } + else { + return vc_merge_rec(a, b); + } + } + vc vc_merge_rec(unsigned a, unsigned b) { + return + vc_merge(ceil2(a), ceil2(b)) + + vc_merge(floor2(a), floor2(b)) + + vc_interleave(ceil2(a) + ceil2(b), floor2(a) + floor2(b)); + } + void split(unsigned n, literal const* ls, literal_vector& even, literal_vector& odd) { + for (unsigned i = 0; i < n; i += 2) { + even.push_back(ls[i]); + } + for (unsigned i = 1; i < n; i += 2) { + odd.push_back(ls[i]); + } + } + + void interleave(literal_vector const& as, + literal_vector const& bs, + literal_vector& out) { + TRACE("pb", tout << "interleave: " << as.size() << " " << bs.size() << "\n";); + SASSERT(as.size() >= bs.size()); + SASSERT(as.size() <= bs.size() + 2); + SASSERT(!as.empty()); + out.push_back(as[0]); + unsigned sz = std::min(as.size()-1, bs.size()); + for (unsigned i = 0; i < sz; ++i) { + literal y1 = mk_max(as[i+1],bs[i]); + literal y2 = mk_min(as[i+1],bs[i]); + psort_nw::cmp(as[i+1], bs[i], y1, y2); + out.push_back(y1); + out.push_back(y2); + } + if (as.size() == bs.size()) { + out.push_back(bs[sz]); + } + else if (as.size() == bs.size() + 2) { + out.push_back(as[sz+1]); + } + SASSERT(out.size() == as.size() + bs.size()); + TRACE("pb", tout << "interleave: " << as.size() << " " << bs.size() << "\n"; + pp(tout << "a: ", as) << "\n"; + pp(tout << "b: ", bs) << "\n"; + pp(tout << "out: ", out) << "\n";); + + } + vc vc_interleave(unsigned a, unsigned b) { + return vc_cmp()*std::min(a-1,b); + } + + void sorting(unsigned n, literal const* xs, literal_vector& out) { + TRACE("pb", tout << "sorting: " << n << "\n";); + switch(n) { + case 0: + break; + case 1: + out.push_back(xs[0]); + break; + case 2: + psort_nw::merge(1, xs, 1, xs+1, out); + break; + default: + if (use_dsorting(n)) { + dsorting(n, n, xs, out); + } + else { + literal_vector out1, out2; + unsigned l = n/2; // TBD + sorting(l, xs, out1); + sorting(n-l, xs+l, out2); + merge(out1.size(), out1.c_ptr(), + out2.size(), out2.c_ptr(), + out); + } + break; + } + TRACE("pb", tout << "sorting: " << n << "\n"; + pp(tout << "in:", n, xs) << "\n"; + pp(tout << "out:", out) << "\n";); + + } + vc vc_sorting(unsigned n) { + switch(n) { + case 0: return vc(0,0); + case 1: return vc(0,0); + case 2: return vc_merge(1,1); + default: + if (use_dsorting(n)) { + return vc_dsorting(n, n); + } + else { + return vc_sorting_rec(n); + } + } + } + vc vc_sorting_rec(unsigned n) { + SASSERT(n > 2); + unsigned l = n/2; + return vc_sorting(l) + vc_sorting(n-l) + vc_merge(l, n-l); + } + + bool use_dsorting(unsigned n) { + SASSERT(n > 2); + return m_force_dsorting || + (!m_disable_dsorting && n < 10 && vc_dsorting(n, n) < vc_sorting_rec(n)); + } + + void smerge(unsigned c, + unsigned a, literal const* as, + unsigned b, literal const* bs, + literal_vector& out) { + TRACE("pb", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n";); + if (a == 1 && b == 1 && c == 1) { + literal y = mk_max(as[0], bs[0]); + if (m_t != GE) { + // x1 <= mk_max(x1,x2) + // x2 <= mk_max(x1,x2) + add_clause(ctx.mk_not(as[0]), y); + add_clause(ctx.mk_not(bs[0]), y); + } + if (m_t != LE) { + // mk_max(x1,x2) <= x1, x2 + add_clause(ctx.mk_not(y), as[0], bs[0]); + } + out.push_back(y); + } + else if (a == 0) { + out.append(std::min(c, b), bs); + } + else if (b == 0) { + out.append(std::min(c, a), as); + } + else if (a > c) { + smerge(c, c, as, b, bs, out); + } + else if (b > c) { + smerge(c, a, as, c, bs, out); + } + else if (a + b <= c) { + merge(a, as, b, bs, out); + } + else if (use_dsmerge(a, b, c)) { + dsmerge(c, a, as, b, bs, out); + } + else { + literal_vector even_a, odd_a; + literal_vector even_b, odd_b; + literal_vector out1, out2; + split(a, as, even_a, odd_a); + split(b, bs, even_b, odd_b); + SASSERT(!even_a.empty()); + SASSERT(!even_b.empty()); + unsigned c1, c2; + if (even(c)) { + c1 = 1 + c/2; c2 = c/2; + } + else { + c1 = (c + 1)/2; c2 = (c - 1)/2; + } + smerge(c1, even_a.size(), even_a.c_ptr(), + even_b.size(), even_b.c_ptr(), out1); + smerge(c2, odd_a.size(), odd_a.c_ptr(), + odd_b.size(), odd_b.c_ptr(), out2); + SASSERT(out1.size() == std::min(even_a.size()+even_b.size(), c1)); + SASSERT(out2.size() == std::min(odd_a.size()+odd_b.size(), c2)); + literal y; + if (even(c)) { + literal z1 = out1.back(); + literal z2 = out2.back(); + out1.pop_back(); + out2.pop_back(); + y = mk_max(z1, z2); + if (m_t != GE) { + add_clause(ctx.mk_not(z1), y); + add_clause(ctx.mk_not(z2), y); + } + if (m_t != LE) { + add_clause(ctx.mk_not(y), z1, z2); + } + } + interleave(out1, out2, out); + if (even(c)) { + out.push_back(y); + } + } + TRACE("pb", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n"; + pp(tout << "a:", a, as) << "\n"; + pp(tout << "b:", b, bs) << "\n"; + pp(tout << "out:", out) << "\n"; + ); + SASSERT(out.size() == std::min(a + b, c)); + } + + vc vc_smerge(unsigned a, unsigned b, unsigned c) { + if (a == 1 && b == 1 && c == 1) { + vc v(1,0); + if (m_t != GE) v = v + vc(0, 2); + if (m_t != LE) v = v + vc(0, 1); + return v; + } + if (a == 0 || b == 0) return vc(0, 0); + if (a > c) return vc_smerge(c, b, c); + if (b > c) return vc_smerge(a, c, c); + if (a + b <= c) return vc_merge(a, b); + if (use_dsmerge(a, b, c)) return vc_dsmerge(a, b, c); + return vc_smerge_rec(a, b, c); + } + vc vc_smerge_rec(unsigned a, unsigned b, unsigned c) { + return + vc_smerge(ceil2(a), ceil2(b), even(c)?(1+c/2):((c+1)/2)) + + vc_smerge(floor2(a), floor2(b), even(c)?(c/2):((c-1)/2)) + + vc_interleave(ceil2(a)+ceil2(b),floor2(a)+floor2(b)) + + vc(1, 0) + + ((m_t != GE)?vc(0, 2):vc(0, 0)) + + ((m_t != LE)?vc(0, 1):vc(0, 0)); + } + bool use_dsmerge(unsigned a, unsigned b, unsigned c) { + return + m_force_dsmerge || + (!m_disable_dsmerge && + a < (1 << 15) && b < (1 << 15) && + vc_dsmerge(a, b, a + b) < vc_smerge_rec(a, b, c)); + } + + void dsmerge( + unsigned c, + unsigned a, literal const* as, + unsigned b, literal const* bs, + literal_vector& out) { + TRACE("pb", tout << "dsmerge: c:" << c << " a:" << a << " b:" << b << "\n";); + SASSERT(a <= c); + SASSERT(b <= c); + SASSERT(a + b >= c); + for (unsigned i = 0; i < c; ++i) { + out.push_back(fresh()); + } + if (m_t != GE) { + for (unsigned i = 0; i < a; ++i) { + add_clause(ctx.mk_not(as[i]), out[i]); + } + for (unsigned i = 0; i < b; ++i) { + add_clause(ctx.mk_not(bs[i]), out[i]); + } + for (unsigned i = 1; i <= a; ++i) { + for (unsigned j = 1; j <= b && i + j <= c; ++j) { + add_clause(ctx.mk_not(as[i-1]),ctx.mk_not(bs[j-1]),out[i+j-1]); + } + } + } + if (m_t != LE) { + literal_vector ls; + for (unsigned k = 0; k < c; ++k) { + ls.reset(); + ls.push_back(ctx.mk_not(out[k])); + if (a <= k) { + add_clause(ctx.mk_not(out[k]), bs[k-a]); + } + if (b <= k) { + add_clause(ctx.mk_not(out[k]), as[k-b]); + } + for (unsigned i = 0; i < std::min(a,k + 1); ++i) { + unsigned j = k - i; + SASSERT(i + j == k); + if (j < b) { + ls.push_back(as[i]); + ls.push_back(bs[j]); + add_clause(ls.size(), ls.c_ptr()); + ls.pop_back(); + ls.pop_back(); + } + } + } + } + } + vc vc_dsmerge(unsigned a, unsigned b, unsigned c) { + vc v(c, 0); + if (m_t != GE) { + v = v + vc(0, a + b + std::min(a, c)*std::min(b, c)/2); + } + if (m_t != LE) { + v = v + vc(0, std::min(a, c)*std::min(b, c)/2); + } + return v; + } + + + void dsorting(unsigned m, unsigned n, literal const* xs, + literal_vector& out) { + TRACE("pb", tout << "dsorting m: " << m << " n: " << n << "\n";); + SASSERT(m <= n); + literal_vector lits; + for (unsigned i = 0; i < m; ++i) { + out.push_back(fresh()); + } + if (m_t != GE) { + for (unsigned k = 1; k <= m; ++k) { + lits.push_back(out[k-1]); + add_subset(true, k, 0, lits, n, xs); + lits.pop_back(); + } + } + if (m_t != LE) { + for (unsigned k = 1; k <= m; ++k) { + lits.push_back(ctx.mk_not(out[k-1])); + add_subset(false, n-k+1, 0, lits, n, xs); + lits.pop_back(); + } + } + } + vc vc_dsorting(unsigned m, unsigned n) { + SASSERT(m <= n && n < 10); + vc v(m, 0); + if (m_t != GE) { + v = v + vc(0, power2(n-1)); + } + if (m_t != LE) { + v = v + vc(0, power2(n-1)); + } + return v; + } + + void add_subset(bool polarity, unsigned k, unsigned offset, literal_vector& lits, + unsigned n, literal const* xs) { + TRACE("pb", tout << "k:" << k << " offset: " << offset << " n: " << n << " "; + pp(tout, lits) << "\n";); + SASSERT(k + offset <= n); + if (k == 0) { + add_clause(lits.size(), lits.c_ptr()); + return; + } + for (unsigned i = offset; i < n - k + 1; ++i) { + lits.push_back(polarity?ctx.mk_not(xs[i]):xs[i]); + add_subset(polarity, k-1, i+1, lits, n, xs); + lits.pop_back(); + } + } + }; + +#endif diff --git a/src/util/stopwatch.h b/src/util/stopwatch.h index 425a13567..2b3c24911 100644 --- a/src/util/stopwatch.h +++ b/src/util/stopwatch.h @@ -69,6 +69,7 @@ public: #undef ARRAYSIZE #define ARRAYSIZE ARRAYSIZE_TEMP #undef max +#undef min #elif defined(__APPLE__) && defined (__MACH__) // Mac OS X diff --git a/src/util/trace.cpp b/src/util/trace.cpp index 3410febd5..72984d808 100644 --- a/src/util/trace.cpp +++ b/src/util/trace.cpp @@ -23,8 +23,8 @@ Revision History: std::ofstream tout(".z3-trace"); #endif -bool g_enable_all_trace_tags = false; -str_hashtable* g_enabled_trace_tags = 0; +static bool g_enable_all_trace_tags = false; +static str_hashtable* g_enabled_trace_tags = 0; static str_hashtable& get_enabled_trace_tags() { if (!g_enabled_trace_tags) { diff --git a/src/util/trace.h b/src/util/trace.h index 0c8c2e5b6..e92eb9d44 100644 --- a/src/util/trace.h +++ b/src/util/trace.h @@ -24,6 +24,10 @@ Revision History: #undef max #undef min #endif +#ifdef __APPLE__ +#undef max +#undef min +#endif #include #ifdef _TRACE diff --git a/src/ast/trail.h b/src/util/trail.h similarity index 89% rename from src/ast/trail.h rename to src/util/trail.h index ac03f9fce..116b3241e 100644 --- a/src/ast/trail.h +++ b/src/util/trail.h @@ -19,9 +19,9 @@ Revision History: #ifndef _TRAIL_H_ #define _TRAIL_H_ -#include"ast.h" #include"obj_hashtable.h" #include"region.h" +#include"obj_ref.h" template class trail { @@ -208,50 +208,6 @@ public: }; -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(); - } -}; template class push_back_trail : public trail { diff --git a/src/smt/union_find.h b/src/util/union_find.h similarity index 67% rename from src/smt/union_find.h rename to src/util/union_find.h index cb2ce68c8..23d33a442 100644 --- a/src/smt/union_find.h +++ b/src/util/union_find.h @@ -20,6 +20,7 @@ Revision History: #define _UNION_FIND_H_ #include "trail.h" +#include "trace.h" class union_find_default_ctx { public: @@ -98,6 +99,7 @@ public: unsigned get_num_vars() const { return m_find.size(); } + unsigned find(unsigned v) const { while (true) { unsigned new_v = m_find[v]; @@ -109,6 +111,8 @@ public: unsigned next(unsigned v) const { return m_next[v]; } + unsigned size(unsigned v) const { return m_size[find(v)]; } + bool is_root(unsigned v) const { return m_find[v] == v; } void merge(unsigned v1, unsigned v2) { @@ -128,10 +132,23 @@ public: CASSERT("union_find", check_invariant()); } + // dissolve equivalence class of v + // this method cannot be used with backtracking. + void dissolve(unsigned v) { + unsigned w; + do { + w = next(v); + m_size[v] = 1; + m_find[v] = v; + m_next[v] = v; + } + while (w != v); + } + void display(std::ostream & out) const { unsigned num = get_num_vars(); for (unsigned v = 0; v < num; v++) { - out << "v" << v << " --> v" << m_find[v] << "\n"; + out << "v" << v << " --> v" << m_find[v] << " (" << size(v) << ")\n"; } } @@ -156,5 +173,71 @@ public: #endif }; + +class basic_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(); + } +}; + + #endif /* _UNION_FIND_H_ */