You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							338 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							338 lines
						
					
					
						
							14 KiB
						
					
					
				| #include "src/solver/Z3Smtsolver.h" | |
|  | |
| #include "src/exceptions/NotSupportedException.h" | |
| #include "src/exceptions/InvalidStateException.h" | |
|  | |
| namespace storm { | |
| 	namespace solver { | |
| #ifdef STORM_HAVE_Z3 | |
| 		Z3SmtSolver::Z3ModelReference::Z3ModelReference(storm::expressions::ExpressionManager const& manager, z3::model model, storm::adapters::Z3ExpressionAdapter& expressionAdapter) : ModelReference(manager), model(model), expressionAdapter(expressionAdapter) { | |
|             // Intentionally left empty. | |
| 		} | |
| #endif | |
|  | |
|         bool Z3SmtSolver::Z3ModelReference::getBooleanValue(storm::expressions::Variable const& variable) const { | |
| #ifdef STORM_HAVE_Z3 | |
|             STORM_LOG_ASSERT(variable.getManager() == this->getManager(), "Requested variable is managed by a different manager."); | |
| 			z3::expr z3Expr = this->expressionAdapter.translateExpression(variable); | |
| 			z3::expr z3ExprValuation = model.eval(z3Expr, true); | |
| 			return this->expressionAdapter.translateExpression(z3ExprValuation).isTrue(); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
|         int_fast64_t Z3SmtSolver::Z3ModelReference::getIntegerValue(storm::expressions::Variable const& variable) const { | |
| #ifdef STORM_HAVE_Z3 | |
|             STORM_LOG_ASSERT(variable.getManager() == this->getManager(), "Requested variable is managed by a different manager."); | |
| 			z3::expr z3Expr = this->expressionAdapter.translateExpression(variable); | |
| 			z3::expr z3ExprValuation = model.eval(z3Expr, true); | |
| 			return this->expressionAdapter.translateExpression(z3ExprValuation).evaluateAsInt(); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
|         double Z3SmtSolver::Z3ModelReference::getRationalValue(storm::expressions::Variable const& variable) const { | |
| #ifdef STORM_HAVE_Z3 | |
|             STORM_LOG_ASSERT(variable.getManager() == this->getManager(), "Requested variable is managed by a different manager."); | |
| 			z3::expr z3Expr = this->expressionAdapter.translateExpression(variable); | |
| 			z3::expr z3ExprValuation = model.eval(z3Expr, true); | |
| 			return this->expressionAdapter.translateExpression(z3ExprValuation).evaluateAsDouble(); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
|          | |
| 		Z3SmtSolver::Z3SmtSolver(storm::expressions::ExpressionManager& manager) | |
| #ifdef STORM_HAVE_Z3 | |
|         : SmtSolver(manager), context(nullptr), solver(nullptr), expressionAdapter(nullptr), lastCheckAssumptions(false), lastResult(CheckResult::Unknown) | |
| #endif | |
| 		{ | |
|             z3::config config; | |
|             config.set("model", true); | |
|             context = std::unique_ptr<z3::context>(new z3::context(config)); | |
|             solver = std::unique_ptr<z3::solver>(new z3::solver(*context)); | |
|             expressionAdapter = std::unique_ptr<storm::adapters::Z3ExpressionAdapter>(new storm::adapters::Z3ExpressionAdapter(this->getManager(), *context)); | |
|         } | |
|          | |
| 		Z3SmtSolver::~Z3SmtSolver() { | |
|             // Intentionally left empty. | |
|         } | |
| 
 | |
| 		void Z3SmtSolver::push() | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			this->solver->push(); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		void Z3SmtSolver::pop() | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			this->solver->pop(); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		void Z3SmtSolver::pop(uint_fast64_t n) | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			this->solver->pop(static_cast<unsigned int>(n)); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		void Z3SmtSolver::reset() | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			this->solver->reset(); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		void Z3SmtSolver::add(storm::expressions::Expression const& assertion) | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			this->solver->add(expressionAdapter->translateExpression(assertion)); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		SmtSolver::CheckResult Z3SmtSolver::check() | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			lastCheckAssumptions = false; | |
| 			switch (this->solver->check()) { | |
| 				case z3::sat: | |
| 					this->lastResult = SmtSolver::CheckResult::Sat; | |
| 					break; | |
| 				case z3::unsat: | |
| 					this->lastResult = SmtSolver::CheckResult::Unsat; | |
| 					break; | |
| 				default: | |
| 					this->lastResult = SmtSolver::CheckResult::Unknown; | |
| 					break; | |
| 			} | |
| 			return this->lastResult; | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		SmtSolver::CheckResult Z3SmtSolver::checkWithAssumptions(std::set<storm::expressions::Expression> const& assumptions) | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			lastCheckAssumptions = true; | |
| 			z3::expr_vector z3Assumptions(*this->context); | |
| 
 | |
| 			for (storm::expressions::Expression assumption : assumptions) { | |
| 				z3Assumptions.push_back(this->expressionAdapter->translateExpression(assumption)); | |
| 			} | |
| 
 | |
| 			switch (this->solver->check(z3Assumptions)) { | |
| 				case z3::sat: | |
| 					this->lastResult = SmtSolver::CheckResult::Sat; | |
| 					break; | |
| 				case z3::unsat: | |
| 					this->lastResult = SmtSolver::CheckResult::Unsat; | |
| 					break; | |
| 				default: | |
| 					this->lastResult = SmtSolver::CheckResult::Unknown; | |
| 					break; | |
| 			} | |
| 			return this->lastResult; | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		SmtSolver::CheckResult Z3SmtSolver::checkWithAssumptions(std::initializer_list<storm::expressions::Expression> const& assumptions) | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			lastCheckAssumptions = true; | |
| 			z3::expr_vector z3Assumptions(*this->context); | |
| 
 | |
| 			for (storm::expressions::Expression assumption : assumptions) { | |
| 				z3Assumptions.push_back(this->expressionAdapter->translateExpression(assumption)); | |
| 			} | |
| 
 | |
| 			switch (this->solver->check(z3Assumptions)) { | |
| 				case z3::sat: | |
| 					this->lastResult = SmtSolver::CheckResult::Sat; | |
| 					break; | |
| 				case z3::unsat: | |
| 					this->lastResult = SmtSolver::CheckResult::Unsat; | |
| 					break; | |
| 				default: | |
| 					this->lastResult = SmtSolver::CheckResult::Unknown; | |
| 					break; | |
| 			} | |
| 			return this->lastResult; | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		storm::expressions::SimpleValuation Z3SmtSolver::getModelAsValuation() | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			STORM_LOG_THROW(this->lastResult == SmtSolver::CheckResult::Sat, storm::exceptions::InvalidStateException, "Unable to create model for formula that was not determined to be satisfiable."); | |
| 			return this->convertZ3ModelToValuation(this->solver->get_model()); | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
|          | |
|         std::shared_ptr<SmtSolver::ModelReference> Z3SmtSolver::getModel() { | |
| #ifdef STORM_HAVE_Z3 | |
| 			STORM_LOG_THROW(this->lastResult == SmtSolver::CheckResult::Sat, storm::exceptions::InvalidStateException, "Unable to create model for formula that was not determined to be satisfiable."); | |
|             return std::shared_ptr<SmtSolver::ModelReference>(new Z3ModelReference(this->getManager(), this->solver->get_model(), *this->expressionAdapter)); | |
| #else | |
|             STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
|         } | |
| 
 | |
| #ifdef STORM_HAVE_Z3 | |
| 		storm::expressions::SimpleValuation Z3SmtSolver::convertZ3ModelToValuation(z3::model const& model) { | |
| 			storm::expressions::SimpleValuation stormModel(this->getManager()); | |
| 
 | |
| 			for (unsigned i = 0; i < model.num_consts(); ++i) { | |
| 				z3::func_decl variableI = model.get_const_decl(i); | |
|                 storm::expressions::Variable stormVariable = this->expressionAdapter->getVariable(variableI); | |
| 				storm::expressions::Expression variableInterpretation = this->expressionAdapter->translateExpression(model.get_const_interp(variableI)); | |
| 
 | |
|                 if (variableInterpretation.getType().isBooleanType()) { | |
|                     stormModel.setBooleanValue(this->getManager().getVariable(variableI.name().str()), variableInterpretation.isTrue()); | |
|                 } else if (variableInterpretation.getType().isIntegralType()) { | |
|                     stormModel.setIntegerValue(this->getManager().getVariable(variableI.name().str()), variableInterpretation.evaluateAsInt()); | |
|                 } else if (variableInterpretation.getType().isRationalType()) { | |
|                     stormModel.setRationalValue(this->getManager().getVariable(variableI.name().str()), variableInterpretation.evaluateAsDouble()); | |
|                 } else { | |
|                     STORM_LOG_ASSERT(false, "Variable interpretation in model is not of type bool, int or rational."); | |
|                 } | |
| 			} | |
| 
 | |
| 			return stormModel; | |
| 		} | |
| #endif | |
|  | |
| 		std::vector<storm::expressions::SimpleValuation> Z3SmtSolver::allSat(std::vector<storm::expressions::Variable> const& important) | |
| 		{ | |
| #ifdef STORM_HAVE_Z3 | |
| 			std::vector<storm::expressions::SimpleValuation> valuations; | |
| 			this->allSat(important, [&valuations](storm::expressions::SimpleValuation const& valuation) -> bool { valuations.push_back(valuation); return true; }); | |
| 			return valuations; | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		uint_fast64_t Z3SmtSolver::allSat(std::vector<storm::expressions::Variable> const& important, std::function<bool(storm::expressions::SimpleValuation&)> const& callback) { | |
| #ifdef STORM_HAVE_Z3 | |
| 			for (storm::expressions::Variable const& variable : important) { | |
|                 STORM_LOG_THROW(variable.hasBooleanType(), storm::exceptions::InvalidArgumentException, "The important atoms for AllSat must be boolean variables."); | |
| 			} | |
| 
 | |
| 			uint_fast64_t numberOfModels = 0; | |
| 			bool proceed = true; | |
| 
 | |
|             // Save the current assertion stack, to be able to backtrack after the procedure. | |
| 			this->push(); | |
| 
 | |
|             // Enumerate models as long as the conjunction is satisfiable and the callback has not aborted the enumeration. | |
| 			while (proceed && this->check() == CheckResult::Sat) { | |
| 				++numberOfModels; | |
| 				z3::model model = this->solver->get_model(); | |
| 
 | |
| 				z3::expr modelExpr = this->context->bool_val(true); | |
| 				storm::expressions::SimpleValuation valuation(this->getManager()); | |
| 
 | |
| 				for (storm::expressions::Variable const& importantAtom : important) { | |
| 					z3::expr z3ImportantAtom = this->expressionAdapter->translateExpression(importantAtom.getExpression()); | |
| 					z3::expr z3ImportantAtomValuation = model.eval(z3ImportantAtom, true); | |
| 					modelExpr = modelExpr && (z3ImportantAtom == z3ImportantAtomValuation); | |
|                     valuation.setBooleanValue(importantAtom, this->expressionAdapter->translateExpression(z3ImportantAtomValuation).isTrue()); | |
| 				} | |
| 
 | |
|                 // Check if we are required to proceed, and if so rule out the current model. | |
| 				proceed = callback(valuation); | |
|                 if (proceed) { | |
|                     this->solver->add(!modelExpr); | |
|                 } | |
| 			} | |
| 
 | |
|             // Restore the old assertion stack and return. | |
| 			this->pop(); | |
| 			return numberOfModels; | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		uint_fast64_t Z3SmtSolver::allSat(std::vector<storm::expressions::Variable> const& important, std::function<bool(SmtSolver::ModelReference&)> const& callback) { | |
| #ifdef STORM_HAVE_Z3 | |
|             for (storm::expressions::Variable const& variable : important) { | |
|                 STORM_LOG_THROW(variable.hasBooleanType(), storm::exceptions::InvalidArgumentException, "The important atoms for AllSat must be boolean variables."); | |
|             } | |
| 
 | |
| 			uint_fast64_t numberOfModels = 0; | |
| 			bool proceed = true; | |
| 
 | |
|             // Save the current assertion stack, to be able to backtrack after the procedure. | |
| 			this->push(); | |
| 
 | |
|             // Enumerate models as long as the conjunction is satisfiable and the callback has not aborted the enumeration. | |
| 			while (proceed && this->check() == CheckResult::Sat) { | |
| 				++numberOfModels; | |
| 				z3::model model = this->solver->get_model(); | |
| 
 | |
| 				z3::expr modelExpr = this->context->bool_val(true); | |
| 				storm::expressions::SimpleValuation valuation(this->getManager()); | |
| 
 | |
| 				for (storm::expressions::Variable const& importantAtom : important) { | |
| 					z3::expr z3ImportantAtom = this->expressionAdapter->translateExpression(importantAtom.getExpression()); | |
| 					z3::expr z3ImportantAtomValuation = model.eval(z3ImportantAtom, true); | |
| 					modelExpr = modelExpr && (z3ImportantAtom == z3ImportantAtomValuation); | |
| 				} | |
| 				Z3ModelReference modelRef(this->getManager(), model, *expressionAdapter); | |
| 
 | |
|                 // Check if we are required to proceed, and if so rule out the current model. | |
| 				proceed = callback(modelRef); | |
|                 if (proceed) { | |
|                     this->solver->add(!modelExpr); | |
|                 } | |
| 			} | |
| 
 | |
| 			this->pop(); | |
| 			return numberOfModels; | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 
 | |
| 		std::vector<storm::expressions::Expression> Z3SmtSolver::getUnsatAssumptions() { | |
| #ifdef STORM_HAVE_Z3 | |
|             STORM_LOG_THROW(lastResult == SmtSolver::CheckResult::Unsat, storm::exceptions::InvalidStateException, "Unable to generate unsatisfiable core of assumptions, because the last check did not determine the formulas to be unsatisfiable.") | |
|             STORM_LOG_THROW(lastCheckAssumptions, storm::exceptions::InvalidStateException, "Unable to generate unsatisfiable core of assumptions, because the last check did not involve assumptions."); | |
| 
 | |
| 			z3::expr_vector z3UnsatAssumptions = this->solver->unsat_core(); | |
| 			std::vector<storm::expressions::Expression> unsatAssumptions; | |
| 			 | |
| 			for (unsigned int i = 0; i < z3UnsatAssumptions.size(); ++i) { | |
| 				unsatAssumptions.push_back(this->expressionAdapter->translateExpression(z3UnsatAssumptions[i])); | |
| 			} | |
| 
 | |
| 			return unsatAssumptions; | |
| #else | |
| 			STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "StoRM is compiled without Z3 support."); | |
| #endif | |
| 		} | |
| 	} | |
| }
 |