// Copyright 2010-2021 Google LLC // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "ortools/math_opt/cpp/solution.h" #include #include #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "ortools/base/logging.h" #include "ortools/base/strong_int.h" #include "ortools/math_opt/core/model_storage.h" #include "ortools/math_opt/core/sparse_vector_view.h" #include "ortools/math_opt/cpp/linear_constraint.h" #include "ortools/math_opt/cpp/variable_and_expressions.h" #include "ortools/math_opt/solution.pb.h" #include "ortools/math_opt/sparse_containers.pb.h" namespace operations_research { namespace math_opt { namespace { template IdMap ValuesFrom(const ModelStorage* const model, const SparseDoubleVectorProto& vars_proto) { return IdMap( model, MakeView(vars_proto).as_map()); } template IdMap BasisValues( const ModelStorage* const model, const SparseBasisStatusVector& basis_proto) { absl::flat_hash_map id_map; for (const auto& [id, basis_status_proto] : MakeView(basis_proto)) { // CHECK fails on BASIS_STATUS_UNSPECIFIED (the validation code should have // tested that). // We need to cast because the C++ proto API stores repeated enums as ints. // // On top of that iOS 11 does not support .value() on optionals so we must // use operator*. const std::optional basis_status = EnumFromProto(static_cast(basis_status_proto)); CHECK(basis_status.has_value()); id_map[static_cast(id)] = *basis_status; } return IdMap(model, std::move(id_map)); } } // namespace std::optional Enum::ToOptString( SolutionStatus value) { switch (value) { case SolutionStatus::kFeasible: return "feasible"; case SolutionStatus::kInfeasible: return "infeasible"; case SolutionStatus::kUndetermined: return "undetermined"; } return std::nullopt; } absl::Span Enum::AllValues() { static constexpr SolutionStatus kSolutionStatusValues[] = { SolutionStatus::kFeasible, SolutionStatus::kInfeasible, SolutionStatus::kUndetermined, }; return absl::MakeConstSpan(kSolutionStatusValues); } std::optional Enum::ToOptString( BasisStatus value) { switch (value) { case BasisStatus::kFree: return "free"; case BasisStatus::kAtLowerBound: return "at_lower_bound"; case BasisStatus::kAtUpperBound: return "at_upper_bound"; case BasisStatus::kFixedValue: return "fixed_value"; case BasisStatus::kBasic: return "basic"; } return std::nullopt; } absl::Span Enum::AllValues() { static constexpr BasisStatus kBasisStatusValues[] = { BasisStatus::kFree, BasisStatus::kAtLowerBound, BasisStatus::kAtUpperBound, BasisStatus::kFixedValue, BasisStatus::kBasic, }; return absl::MakeConstSpan(kBasisStatusValues); } PrimalSolution PrimalSolution::FromProto( const ModelStorage* model, const PrimalSolutionProto& primal_solution_proto) { PrimalSolution primal_solution; primal_solution.variable_values = ValuesFrom(model, primal_solution_proto.variable_values()); primal_solution.objective_value = primal_solution_proto.objective_value(); // TODO(b/209014770): consider adding a function to simplify this pattern. const std::optional feasibility_status = EnumFromProto(primal_solution_proto.feasibility_status()); CHECK(feasibility_status.has_value()); primal_solution.feasibility_status = *feasibility_status; return primal_solution; } PrimalRay PrimalRay::FromProto(const ModelStorage* model, const PrimalRayProto& primal_ray_proto) { return {.variable_values = ValuesFrom(model, primal_ray_proto.variable_values())}; } DualSolution DualSolution::FromProto( const ModelStorage* model, const DualSolutionProto& dual_solution_proto) { DualSolution dual_solution; dual_solution.dual_values = ValuesFrom(model, dual_solution_proto.dual_values()); dual_solution.reduced_costs = ValuesFrom(model, dual_solution_proto.reduced_costs()); if (dual_solution_proto.has_objective_value()) { dual_solution.objective_value = dual_solution_proto.objective_value(); } // TODO(b/209014770): consider adding a function to simplify this pattern. const std::optional feasibility_status = EnumFromProto(dual_solution_proto.feasibility_status()); CHECK(feasibility_status.has_value()); dual_solution.feasibility_status = *feasibility_status; return dual_solution; } DualRay DualRay::FromProto(const ModelStorage* model, const DualRayProto& dual_ray_proto) { return {.dual_values = ValuesFrom(model, dual_ray_proto.dual_values()), .reduced_costs = ValuesFrom(model, dual_ray_proto.reduced_costs())}; } Basis Basis::FromProto(const ModelStorage* model, const BasisProto& basis_proto) { Basis basis; basis.constraint_status = BasisValues(model, basis_proto.constraint_status()); basis.variable_status = BasisValues(model, basis_proto.variable_status()); // TODO(b/209014770): consider adding a function to simplify this pattern. const std::optional basic_dual_feasibility = EnumFromProto(basis_proto.basic_dual_feasibility()); CHECK(basic_dual_feasibility.has_value()); basis.basic_dual_feasibility = *basic_dual_feasibility; return basis; } Solution Solution::FromProto(const ModelStorage* model, const SolutionProto& solution_proto) { Solution solution; if (solution_proto.has_primal_solution()) { solution.primal_solution = PrimalSolution::FromProto(model, solution_proto.primal_solution()); } if (solution_proto.has_dual_solution()) { solution.dual_solution = DualSolution::FromProto(model, solution_proto.dual_solution()); } if (solution_proto.has_basis()) { solution.basis = Basis::FromProto(model, solution_proto.basis()); } return solution; } } // namespace math_opt } // namespace operations_research