Copyright (c) 2015 Microsoft Corporation
--*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;
using Microsoft.Z3;
using Microsoft.SolverFoundation.Common;
using Microsoft.SolverFoundation.Services;
using Microsoft.SolverFoundation.Plugin;
namespace Microsoft.SolverFoundation.Plugin.Z3
{
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
public Z3MILPSolver() : base(null)
{
_result = LinearResult.Feasible;
_solver = new Z3BaseSolver(this);
}
public Z3MILPSolver(ISolverEnvironment context) : this() { }
public void Shutdown() { _solver.DestructSolver(true); }
#endregion Solver construction and destruction
#region Obtaining information about the solution
public ILinearSolverReport GetReport(LinearSolverReportType reportType)
{
return null;
}
#endregion Obtaining information about the solution
#region Construction of the problem
private ArithExpr MkGoalRow(int rid)
{
List<ArithExpr> row = new List<ArithExpr>();
foreach (LinearEntry entry in GetRowEntries(rid))
{
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());
}
}
private void AddRow(int rid)
{
ArithExpr row = MkGoalRow(rid);
_solver.AssertArith(rid, row);
}
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
public ILinearSolution Solve(ISolverParameters 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;
}
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);
}
}
public class Z3LinearSolverReport : LinearReport
{
public Z3LinearSolverReport(SolverContext context, ISolver solver, Solution solution, LinearSolutionMapping solutionMapping)
: base(context, solver, solution, solutionMapping) {
}
}
}