Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sat_inprocessing.h
Go to the documentation of this file.
1// Copyright 2010-2025 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// This file contains the entry point for our presolve/inprocessing code.
15//
16// TODO(user): for now it is mainly presolve, but the idea is to call these
17// function during the search so they should be as incremental as possible. That
18// is avoid doing work that is not useful because nothing changed or exploring
19// parts that were not done during the last round.
20
21#ifndef ORTOOLS_SAT_SAT_INPROCESSING_H_
22#define ORTOOLS_SAT_SAT_INPROCESSING_H_
23
24#include <array>
25#include <cstddef>
26#include <cstdint>
27#include <deque>
28#include <vector>
29
30#include "absl/container/flat_hash_map.h"
31#include "absl/hash/hash.h"
32#include "absl/types/span.h"
34#include "ortools/sat/clause.h"
39#include "ortools/sat/model.h"
40#include "ortools/sat/probing.h"
47#include "ortools/sat/util.h"
49#include "ortools/util/bitset.h"
54
55namespace operations_research {
56namespace sat {
57
58// The order is important and each clauses has a "special" literal that is
59// put first.
60//
61// TODO(user): Use a flat memory structure instead.
63 // Utility function that push back clause but also make sure the given literal
64 // from clause appear first.
66 absl::Span<const Literal> clause);
67
68 std::deque<std::vector<Literal>> clauses;
69};
70
75
77 // The time budget to spend.
79
80 // We assume this is also true if --v 1 is activated and we actually log
81 // even more in --v 1.
82 bool log_info = false;
83
84 // See ProbingOptions in probing.h
86
87 // Whether we perform a transitive reduction of the binary implication graph
88 // after equivalent literal detection and before each probing pass.
89 //
90 // TODO(user): Doing that before the current SAT presolve also change the
91 // possible reduction. This shouldn't matter if we use the binary implication
92 // graph and its reachability instead of just binary clause though.
94
96};
97
98// We need to keep some information from one call to the next, so we use a
99// class. Note that as this becomes big, we can probably split it.
100//
101// TODO(user): Some algorithms here use the normal SAT propagation engine.
102// However we might want to temporarily disable activities/phase saving and so
103// on if we want to run them as in-processing steps so that they
104// do not "pollute" the normal SAT search.
105//
106// TODO(user): For the propagation, this depends on the SatSolver class, which
107// mean we cannot really use it without some refactoring as an in-processing
108// from the SatSolver::Solve() function. So we do need a special
109// InprocessingSolve() that lives outside SatSolver. Alternatively, we can
110// extract the propagation main loop and conflict analysis from SatSolver.
112 public:
113 explicit Inprocessing(Model* model)
114 : assignment_(model->GetOrCreate<Trail>()->Assignment()),
115 params_(*model->GetOrCreate<SatParameters>()),
116 implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
117 clause_manager_(model->GetOrCreate<ClauseManager>()),
118 lrat_proof_handler_(model->Mutable<LratProofHandler>()),
119 trail_(model->GetOrCreate<Trail>()),
120 decision_policy_(model->GetOrCreate<SatDecisionPolicy>()),
121 time_limit_(model->GetOrCreate<TimeLimit>()),
122 sat_solver_(model->GetOrCreate<SatSolver>()),
123 all_lp_constraints_(
124 model->GetOrCreate<LinearProgrammingConstraintCollection>()),
125 stamping_simplifier_(model->GetOrCreate<StampingSimplifier>()),
126 blocked_clause_simplifier_(
127 model->GetOrCreate<BlockedClauseSimplifier>()),
128 bounded_variable_elimination_(
129 model->GetOrCreate<BoundedVariableElimination>()),
130 congruence_closure_(model->GetOrCreate<GateCongruenceClosure>()),
131 vivifier_(model->GetOrCreate<Vivifier>()),
132 equivalence_sat_sweeping_(model->GetOrCreate<EquivalenceSatSweeping>()),
133 failed_literal_probing_(model->GetOrCreate<FailedLiteralProbing>()),
134 postsolve_(model->GetOrCreate<PostsolveClauses>()),
135 logger_(model->GetOrCreate<SolverLogger>()) {}
136
137 // Does some simplifications until a fix point is reached or the given
138 // deterministic time is passed.
139 bool PresolveLoop(SatPresolveOptions options);
140
141 // When the option use_sat_inprocessing is true, then this is called at each
142 // restart. It is up to the heuristics here to decide what inprocessing we
143 // do or if we wait for the next call before doing anything.
144 bool InprocessingRound();
145
146 // Simple wrapper that makes sure all the clauses are attached before a
147 // propagation is performed.
148 bool LevelZeroPropagate();
149
150 // Detects equivalences in the implication graph and propagates any failed
151 // literal found during the process.
152 bool DetectEquivalencesAndStamp(bool use_transitive_reduction, bool log_info);
153
154 // Removes fixed variables and exploit equivalence relations to cleanup the
155 // clauses. Returns false if UNSAT.
156 bool RemoveFixedAndEquivalentVariables(bool log_info);
157
158 // Returns true if there is new fixed variables or new equivalence relations
159 // since RemoveFixedAndEquivalentVariables() was last called.
160 bool MoreFixedVariableToClean() const;
161 bool MoreRedundantVariableToClean() const;
162
163 // Processes all clauses and see if there is any subsumption/strenghtening
164 // reductions that can be performed. Returns false if UNSAT.
165 bool SubsumeAndStrenghtenRound(bool log_info);
166
167 // A bit hacky. Only needed during refactoring.
168 void ProvideLogger(SolverLogger* logger) { logger_ = logger; }
169
170 private:
171 const VariablesAssignment& assignment_;
172 const SatParameters& params_;
173 BinaryImplicationGraph* implication_graph_;
174 ClauseManager* clause_manager_;
175 LratProofHandler* lrat_proof_handler_;
176 Trail* trail_;
177 SatDecisionPolicy* decision_policy_;
178 TimeLimit* time_limit_;
179 SatSolver* sat_solver_;
180 LinearProgrammingConstraintCollection* all_lp_constraints_;
181 StampingSimplifier* stamping_simplifier_;
182 BlockedClauseSimplifier* blocked_clause_simplifier_;
183 BoundedVariableElimination* bounded_variable_elimination_;
184 GateCongruenceClosure* congruence_closure_;
185 Vivifier* vivifier_;
186 EquivalenceSatSweeping* equivalence_sat_sweeping_;
187 FailedLiteralProbing* failed_literal_probing_;
188 PostsolveClauses* postsolve_;
189 SolverLogger* logger_;
190
191 // Inprocessing dtime.
192 bool first_inprocessing_call_ = true;
193 double reference_dtime_ = 0.0;
194 double total_dtime_ = 0.0;
195
196 // Last since clause database was cleaned up.
197 int64_t last_num_redundant_literals_ = 0;
198 int64_t last_num_fixed_variables_ = 0;
199
200 // Temporary data for LRAT proofs.
201 std::vector<ClauseId> clause_ids_;
202};
203
204// Implements "stamping" as described in "Efficient CNF Simplification based on
205// Binary Implication Graphs", Marijn Heule, Matti Jarvisalo and Armin Biere.
206//
207// This sample the implications graph with a spanning tree, and then simplify
208// all clauses (subsumption / strengthening) using the implications encoded in
209// this tree. So this allows to consider chain of implications instead of just
210// direct ones, but depending on the problem, only a small fraction of the
211// implication graph will be captured by the tree.
212//
213// Note that we randomize the spanning tree at each call. This can benefit by
214// having the implication graph be transitively reduced before.
216 public:
217 explicit StampingSimplifier(Model* model)
218 : assignment_(model->GetOrCreate<Trail>()->Assignment()),
219 trail_(model->GetOrCreate<Trail>()),
220 implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
221 clause_manager_(model->GetOrCreate<ClauseManager>()),
222 lrat_proof_handler_(model->Mutable<LratProofHandler>()),
223 random_(model->GetOrCreate<ModelRandomGenerator>()),
224 time_limit_(model->GetOrCreate<TimeLimit>()) {}
225
226 // This is "fast" (linear scan + sort of all clauses) so we always complete
227 // the full round.
228 //
229 // TODO(user): To save one scan over all the clauses, we could do the fixed
230 // and equivalence variable cleaning here too.
231 bool DoOneRound(bool log_info);
232
233 // When we compute stamps, we might detect fixed variable (via failed literal
234 // probing in the implication graph). So it might make sense to do that until
235 // we have dealt with all fixed literals before calling DoOneRound().
236 bool ComputeStampsForNextRound(bool log_info);
237
238 // Visible for testing.
240
241 // Using a DFS visiting order, we can answer reachability query in O(1) on a
242 // tree, this is well known. ComputeStamps() also detect failed literal in
243 // the tree and fix them. It can return false on UNSAT.
244 bool ComputeStamps();
246 return first_stamps_[a.Index()] < first_stamps_[b.Index()] &&
247 last_stamps_[b.Index()] < last_stamps_[a.Index()];
248 }
249
250 bool ProcessClauses();
251
252 private:
253 // Appends the chain of implications from `a` to `b` to `chain`, in order
254 // (a => x => y => ... z => b) if `reversed` is false, or in reverse order
255 // (not(b) => not(z) => ... not(y) => not(x) => not(a)) otherwise. b must be a
256 // descendant of a.
257 void AppendImplicationChain(Literal a, Literal b,
258 std::vector<ClauseId>& chain,
259 bool reversed = false);
260
261 const VariablesAssignment& assignment_;
262 Trail* trail_;
263 BinaryImplicationGraph* implication_graph_;
264 ClauseManager* clause_manager_;
265 LratProofHandler* lrat_proof_handler_;
266 ModelRandomGenerator* random_;
267 TimeLimit* time_limit_;
268
269 // For ComputeStampsForNextRound().
270 bool stamps_are_already_computed_ = false;
271
272 // Reset at each round.
273 double dtime_ = 0.0;
274 int64_t num_subsumed_clauses_ = 0;
275 int64_t num_removed_literals_ = 0;
276 int64_t num_fixed_ = 0;
277
278 // Encode a spanning tree of the implication graph.
280
281 // Temporary data for the DFS.
283 std::vector<LiteralIndex> dfs_stack_;
284 // Temporary data for LRAT proofs.
285 std::vector<ClauseId> clause_ids_;
286
287 // First/Last visited index in a DFS of the tree above.
290};
291
292// A clause c is "blocked" by a literal l if all clauses containing the
293// negation of l resolve to trivial clause with c. Blocked clause can be
294// simply removed from the problem. At postsolve, if a blocked clause is not
295// satisfied, then l can simply be set to true without breaking any of the
296// clause containing not(l).
297//
298// See the paper "Blocked Clause Elimination", Matti Jarvisalo, Armin Biere,
299// and Marijn Heule.
300//
301// TODO(user): This requires that l only appear in clauses and not in the
302// integer part of CP-SAT.
304 public:
306 : assignment_(model->GetOrCreate<Trail>()->Assignment()),
307 implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
308 clause_manager_(model->GetOrCreate<ClauseManager>()),
309 postsolve_(model->GetOrCreate<PostsolveClauses>()),
310 time_limit_(model->GetOrCreate<TimeLimit>()) {}
311
312 void DoOneRound(bool log_info);
313
314 private:
315 void InitializeForNewRound();
316 void ProcessLiteral(Literal current_literal);
317 bool ClauseIsBlocked(Literal current_literal,
318 absl::Span<const Literal> clause);
319
320 const VariablesAssignment& assignment_;
321 BinaryImplicationGraph* implication_graph_;
322 ClauseManager* clause_manager_;
323 PostsolveClauses* postsolve_;
324 TimeLimit* time_limit_;
325
326 double dtime_ = 0.0;
327 int32_t num_blocked_clauses_ = 0;
328 int64_t num_inspected_literals_ = 0;
329
330 // Temporary vector to mark literal of a clause.
332
333 // List of literal to process.
334 // TODO(user): use priority queue?
336 std::deque<Literal> queue_;
337
338 // We compute the occurrence graph just once at the beginning of each round
339 // and we do not shrink it as we remove blocked clauses.
340 DEFINE_STRONG_INDEX_TYPE(rat_literal_clause_index);
343 literal_to_clauses_;
344};
345
347 public:
349 : parameters_(*model->GetOrCreate<SatParameters>()),
350 assignment_(model->GetOrCreate<Trail>()->Assignment()),
351 implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
352 clause_manager_(model->GetOrCreate<ClauseManager>()),
353 postsolve_(model->GetOrCreate<PostsolveClauses>()),
354 trail_(model->GetOrCreate<Trail>()),
355 time_limit_(model->GetOrCreate<TimeLimit>()) {}
356
357 bool DoOneRound(bool log_info);
358
359 private:
360 int NumClausesContaining(Literal l);
361 void UpdatePriorityQueue(BooleanVariable var);
362 bool CrossProduct(BooleanVariable var);
363 void DeleteClause(SatClause* sat_clause);
364 void DeleteAllClausesContaining(Literal literal);
365 void AddClause(absl::Span<const Literal> clause);
366 bool RemoveLiteralFromClause(Literal lit, SatClause* sat_clause);
367 bool Propagate();
368
369 // The actual clause elimination algo. We have two versions, one just compute
370 // the "score" of what will be the final state. The other perform the
371 // resolution, remove old clauses and add the new ones.
372 //
373 // Returns false on UNSAT.
374 template <bool score_only, bool with_binary_only>
375 bool ResolveAllClauseContaining(Literal lit);
376
377 const SatParameters& parameters_;
378 const VariablesAssignment& assignment_;
379 BinaryImplicationGraph* implication_graph_;
380 ClauseManager* clause_manager_;
381 PostsolveClauses* postsolve_;
382 Trail* trail_;
383 TimeLimit* time_limit_;
384
385 int propagation_index_;
386
387 double dtime_ = 0.0;
388 int64_t num_inspected_literals_ = 0;
389 int64_t num_simplifications_ = 0;
390 int64_t num_blocked_clauses_ = 0;
391 int64_t num_eliminated_variables_ = 0;
392 int64_t num_literals_diff_ = 0;
393 int64_t num_clauses_diff_ = 0;
394
395 int64_t new_score_;
396 int64_t score_threshold_;
397
398 // Temporary vector to mark literal of a clause and compute its resolvant.
400 std::vector<Literal> resolvant_;
401
402 // Priority queue of variable to process.
403 // We will process highest priority first.
404 struct VariableWithPriority {
405 BooleanVariable var;
406 int32_t priority;
407
408 // Interface for the IntegerPriorityQueue.
409 int Index() const { return var.value(); }
410 bool operator<(const VariableWithPriority& o) const {
411 return priority < o.priority;
412 }
413 };
414 IntegerPriorityQueue<VariableWithPriority> queue_;
415
416 // We update the queue_ in batch.
417 util_intops::StrongVector<BooleanVariable, bool> in_need_to_be_updated_;
418 std::vector<BooleanVariable> need_to_be_updated_;
419
420 // We compute the occurrence graph just once at the beginning of each round.
421 // We maintains the sizes at all time and lazily shrink the graph with deleted
422 // clauses.
423 DEFINE_STRONG_INDEX_TYPE(ClauseIndex);
424 util_intops::StrongVector<ClauseIndex, SatClause*> clauses_;
425 util_intops::StrongVector<LiteralIndex, std::vector<ClauseIndex>>
426 literal_to_clauses_;
427 util_intops::StrongVector<LiteralIndex, int> literal_to_num_clauses_;
428};
429
430// If we have a = f(literals) and b = f(same_literals), then a and b are
431// equivalent.
432//
433// This class first detects such relationships, then reaches the "equivalence"
434// fixed point (i.e. closure) by detecting equivalences, then updating the RHS
435// of the relations which might lead to more equivalences and so on.
436//
437// This mostly follows the paper "Clausal Congruence closure", Armin Biere,
438// Katalin Fazekas, Mathias Fleury, Nils Froleyks, 2024.
439//
440// TODO(user): For now we only deal with f() being an and_gate with an arbitrary
441// number of inputs, or equivalently target = product/and(literals). The next
442// most important one is xor().
443//
444// TODO(user): What is the relation with symmetry ? It feel like all the
445// equivalences found here should be in the same symmetry orbit ?
448 public:
450 : assignment_(model->GetOrCreate<Trail>()->Assignment()),
451 sat_solver_(model->GetOrCreate<SatSolver>()),
452 trail_(model->GetOrCreate<Trail>()),
453 implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
454 clause_manager_(model->GetOrCreate<ClauseManager>()),
455 clause_id_generator_(model->GetOrCreate<ClauseIdGenerator>()),
456 lrat_proof_handler_(model->Mutable<LratProofHandler>()),
457 shared_stats_(model->GetOrCreate<SharedStatistics>()),
458 logger_(model->GetOrCreate<SolverLogger>()),
459 time_limit_(model->GetOrCreate<TimeLimit>()) {}
460
462
463 bool DoOneRound(bool log_info);
464
465 private:
466 DEFINE_STRONG_INDEX_TYPE(TruthTableId);
467
468 struct GateHash {
469 explicit GateHash(util_intops::StrongVector<GateId, int>* t,
471 : gates_type(t), gates_inputs(g) {}
472 std::size_t operator()(GateId gate_id) const {
473 return absl::HashOf((*gates_type)[gate_id], (*gates_inputs)[gate_id]);
474 }
476 const CompactVectorVector<GateId, LiteralIndex>* gates_inputs;
477 };
478
479 struct GateEq {
480 explicit GateEq(util_intops::StrongVector<GateId, int>* t,
481 CompactVectorVector<GateId, LiteralIndex>* g)
482 : gates_type(t), gates_inputs(g) {}
483 bool operator()(GateId gate_a, GateId gate_b) const {
484 if (gate_a == gate_b) return true;
485
486 // We use absl::span<> comparison.
487 return ((*gates_type)[gate_a] == (*gates_type)[gate_b]) &&
488 ((*gates_inputs)[gate_a] == (*gates_inputs)[gate_b]);
489 }
490 const util_intops::StrongVector<GateId, int>* gates_type;
491 const CompactVectorVector<GateId, LiteralIndex>* gates_inputs;
492 };
493
494 // Recovers "target_literal = and(literals)" from the model.
495 //
496 // The current algo is pretty basic: For all clauses, look for literals that
497 // imply all the others to false. We only look at direct implications to be
498 // fast.
499 //
500 // This is because such an and_gate is encoded as:
501 // - for all i, target_literal => literal_i (direct binary implication)
502 // - all literal at true => target_literal, this is a clause:
503 // (not(literal[i]) for all i, target_literal).
504 void ExtractAndGatesAndFillShortTruthTables(PresolveTimer& timer);
505
506 // From possible assignment of small set of variables (truth_tables), extract
507 // functions of the form one_var = f(other_vars).
508 void ExtractShortGates(PresolveTimer& timer);
509
510 // Detects gates encoded in the given truth table, and add them to the set
511 // of gates. Returns the number of gate detected.
512 int ProcessTruthTable(
513 absl::Span<const BooleanVariable> inputs, SmallBitset truth_table,
514 absl::Span<const TruthTableId> ids_for_proof,
515 absl::Span<const std::pair<Literal, Literal>> binary_used);
516
517 // Add a small clause to the corresponding truth table.
518 template <int arity>
519 void AddToTruthTable(SatClause* clause,
520 absl::flat_hash_map<std::array<BooleanVariable, arity>,
521 TruthTableId>& ids);
522
523 // Make sure the small gate at given id is canonicalized.
524 // Returns its number of inputs.
525 int CanonicalizeShortGate(GateId id);
526
527 const VariablesAssignment& assignment_;
528 SatSolver* sat_solver_;
529 Trail* trail_;
530 BinaryImplicationGraph* implication_graph_;
531 ClauseManager* clause_manager_;
532 ClauseIdGenerator* clause_id_generator_;
533 LratProofHandler* lrat_proof_handler_;
534 SharedStatistics* shared_stats_;
535 SolverLogger* logger_;
536 TimeLimit* time_limit_;
537
538 SparseBitset<LiteralIndex> marked_;
539 SparseBitset<LiteralIndex> seen_;
540 SparseBitset<LiteralIndex> next_seen_;
541
542 // A Boolean gates correspond to target = f(inputs).
543 //
544 // Note that the inputs are canonicalized. For and_gates, they are sorted,
545 // since the gate function does not depend on the order. The type of an
546 // and_gates is kAndGateType.
547 //
548 // Otherwise, we support generic 2 and 3 inputs gates where the type is the
549 // truth table. i.e. target = type[sum value_of_inputs[i] * 2^i]. For such
550 // gate, the target and inputs will always be canonicalized to positive and
551 // sorted literal. We just update the truth table accordingly.
552 //
553 // If lrat_proof_handler_ != nullptr, we also store all the SatClause* needed
554 // to infer such gate from the clause database. The case of kAndGateType is
555 // special, because we don't have SatClause for the binary clauses used to
556 // infer it. We will thus only store the base clause used, if we have a =
557 // and(x,y,...) we only store the clause "x and y and ... => a".
558 static constexpr int kAndGateType = -1;
559 util_intops::StrongVector<GateId, LiteralIndex> gates_target_;
560 util_intops::StrongVector<GateId, int> gates_type_;
561 CompactVectorVector<GateId, LiteralIndex> gates_inputs_;
562 CompactVectorVector<GateId, const SatClause*> gates_clauses_;
563
564 // Truth tables on 2 variables are handled differently, and we don't use
565 // a TruthTableId indirection.
566 //
567 // TODO(user): it feels like we could benefit from just storing this all
568 // the time in the binary_implication graph. This allow to never add duplicate
569 // and detect easy case of fixing/equivalences right away. To investigate.
570 absl::flat_hash_map<std::array<BooleanVariable, 2>, SmallBitset> ids2_;
571
572 // Map (Xi) (sorted) to a bitmask corresponding to the allowed values.
573 // We loop over all short clauses to fill this. We actually store an "id"
574 // pointing in the vectors below.
575 //
576 // TruthTableIds are assigned in insertion order. We copy the map key in
577 // truth_tables_inputs_, this is a bit wasted but simplify the code.
578 absl::flat_hash_map<std::array<BooleanVariable, 3>, TruthTableId> ids3_;
579 absl::flat_hash_map<std::array<BooleanVariable, 4>, TruthTableId> ids4_;
580 absl::flat_hash_map<std::array<BooleanVariable, 5>, TruthTableId> ids5_;
581 CompactVectorVector<TruthTableId, BooleanVariable> truth_tables_inputs_;
582 util_intops::StrongVector<TruthTableId, SmallBitset> truth_tables_bitset_;
583 CompactVectorVector<TruthTableId, SatClause*> truth_tables_clauses_;
584
585 // Temporary vector used to construct truth_tables_clauses_.
586 std::vector<TruthTableId> tmp_ids_;
587 std::vector<SatClause*> tmp_clauses_;
588
589 // Temporary SatClause* for binary, so we don't need to specialize too much
590 // code for them.
591 std::vector<std::unique_ptr<SatClause>> tmp_binary_clauses_;
592
593 // For stats.
594 double total_dtime_ = 0.0;
595 double total_wtime_ = 0.0;
596 int64_t total_num_units_ = 0;
597 int64_t total_gates_ = 0;
598 int64_t total_equivalences_ = 0;
599};
600
601} // namespace sat
602} // namespace operations_research
603
604#endif // ORTOOLS_SAT_SAT_INPROCESSING_H_
bool DetectEquivalencesAndStamp(bool use_transitive_reduction, bool log_info)
bool RemoveFixedAndEquivalentVariables(bool log_info)
bool PresolveLoop(SatPresolveOptions options)
void ProvideLogger(SolverLogger *logger)
LiteralIndex Index() const
Definition sat_base.h:92
bool ImplicationIsInTree(Literal a, Literal b) const
OR-Tools root namespace.
#define DEFINE_STRONG_INDEX_TYPE(index_type_name)
std::deque< std::vector< Literal > > clauses
void AddClauseWithSpecialLiteral(Literal literal, absl::Span< const Literal > clause)