OR-Tools  9.3
subsolver.h
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// Simple framework for choosing and distributing a solver "sub-tasks" on a set
15// of threads.
16
17#ifndef OR_TOOLS_SAT_SUBSOLVER_H_
18#define OR_TOOLS_SAT_SUBSOLVER_H_
19
20#include <algorithm>
21#include <cmath>
22#include <cstdint>
23#include <functional>
24#include <memory>
25#include <string>
26#include <utility>
27#include <vector>
28
30
31#if !defined(__PORTABLE_PLATFORM__)
33#endif // __PORTABLE_PLATFORM__
34
35namespace operations_research {
36namespace sat {
37
38// The API used for distributing work. Each subsolver can generate tasks and
39// synchronize itself with the rest of the world.
40//
41// Note that currently only the main thread interact with subsolvers. Only the
42// tasks generated by GenerateTask() are executed in parallel in a threadpool.
43class SubSolver {
44 public:
45 explicit SubSolver(const std::string& name) : name_(name) {}
46 virtual ~SubSolver() {}
47
48 // Returns true iff GenerateTask() can be called.
49 //
50 // Note(user): In the current design, a SubSolver is never deleted until the
51 // end of the Solve() that created it. But is is okay to always return false
52 // here and release the memory used by the Subsolver internal if there is no
53 // need to call this Subsolver ever again. The overhead of iterating over it
54 // in the main solver loop should be minimal.
55 virtual bool TaskIsAvailable() = 0;
56
57 // Returns a task to run. The task_id is just an ever increasing counter that
58 // correspond to the number of total calls to GenerateTask().
59 //
60 // TODO(user): We could use a more complex selection logic and pass in the
61 // deterministic time limit this subtask should run for. Unclear at this
62 // stage.
63 virtual std::function<void()> GenerateTask(int64_t task_id) = 0;
64
65 // Synchronizes with the external world from this SubSolver point of view.
66 // Also incorporate the results of the latest completed tasks if any.
67 //
68 // Note(user): The intended implementation for determinism is that tasks
69 // update asynchronously (and so non-deterministically) global "shared"
70 // classes, but this global state is incorporated by the Subsolver only when
71 // Synchronize() is called.
72 virtual void Synchronize() = 0;
73
74 // Returns the score as updated by the completed tasks before the last
75 // Synchronize() call. Everything else being equal, we prefer to run a
76 // SubSolver with the highest score.
77 //
78 // TODO(user): This is unused for now.
79 double score() const { return score_; }
80
81 // Returns the total deterministic time spend by the completed tasks before
82 // the last Synchronize() call.
83 double deterministic_time() const { return deterministic_time_; }
84
85 // Returns the name of this SubSolver. Used in logs.
86 std::string name() const { return name_; }
87
88 // Returns search statistics.
89 virtual std::string StatisticsString() const { return std::string(); }
90
91 protected:
92 const std::string name_;
93 double score_ = 0.0;
94 double deterministic_time_ = 0.0;
95};
96
97// A simple wrapper to add a synchronization point in the list of subsolvers.
99 public:
100 explicit SynchronizationPoint(std::function<void()> f)
101 : SubSolver(""), f_(std::move(f)) {}
102 bool TaskIsAvailable() final { return false; }
103 std::function<void()> GenerateTask(int64_t task_id) final { return nullptr; }
104 void Synchronize() final { f_(); }
105
106 private:
107 std::function<void()> f_;
108};
109
110// Executes the following loop:
111// 1/ Synchronize all in given order.
112// 2/ generate and schedule one task from the current "best" subsolver.
113// 3/ repeat until no extra task can be generated and all tasks are done.
114//
115// The complexity of each selection is in O(num_subsolvers), but that should
116// be okay given that we don't expect more than 100 such subsolvers.
117//
118// Note that it is okay to incorporate "special" subsolver that never produce
119// any tasks. This can be used to synchronize classes used by many subsolvers
120// just once for instance.
122 const std::vector<std::unique_ptr<SubSolver>>& subsolvers, int num_threads);
123
124// Similar to NonDeterministicLoop() except this should result in a
125// deterministic solver provided that all SubSolver respect the Synchronize()
126// contract.
127//
128// Executes the following loop:
129// 1/ Synchronize all in given order.
130// 2/ generate and schedule up to batch_size tasks using an heuristic to select
131// which one to run.
132// 3/ wait for all task to finish.
133// 4/ repeat until no task can be generated in step 2.
135 const std::vector<std::unique_ptr<SubSolver>>& subsolvers, int num_threads,
136 int batch_size);
137
138// Same as above, but specialized implementation for the case num_threads=1.
139// This avoids using a Threadpool altogether. It should have the same behavior
140// than the functions above with num_threads=1 and batch_size=1. Note that an
141// higher batch size will not behave in the same way, even if num_threads=1.
142void SequentialLoop(const std::vector<std::unique_ptr<SubSolver>>& subsolvers);
143
144} // namespace sat
145} // namespace operations_research
146
147#endif // OR_TOOLS_SAT_SUBSOLVER_H_
virtual std::string StatisticsString() const
Definition: subsolver.h:89
virtual std::function< void()> GenerateTask(int64_t task_id)=0
SubSolver(const std::string &name)
Definition: subsolver.h:45
SynchronizationPoint(std::function< void()> f)
Definition: subsolver.h:100
std::function< void()> GenerateTask(int64_t task_id) final
Definition: subsolver.h:103
void DeterministicLoop(const std::vector< std::unique_ptr< SubSolver > > &subsolvers, int num_threads, int batch_size)
Definition: subsolver.cc:93
void SequentialLoop(const std::vector< std::unique_ptr< SubSolver > > &subsolvers)
Definition: subsolver.cc:63
void NonDeterministicLoop(const std::vector< std::unique_ptr< SubSolver > > &subsolvers, int num_threads)
Definition: subsolver.cc:125
Collection of objects used to extend the Constraint Solver library.
STL namespace.