vampire-sys 0.5.2

Low-level FFI bindings to the Vampire theorem prover (use the 'vampire' crate instead)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
/*
 * This file is part of the source code of the software program
 * Vampire. It is protected by applicable
 * copyright laws.
 *
 * This source code is distributed under the licence found here
 * https://vprover.github.io/license.html
 * and in the source directory
 */

#include "PredicateSplitPassiveClauseContainers.hpp"

#include <string>
#include <algorithm>
#include <iterator>
#include <limits>

#include "Shell/Options.hpp"
#include "Kernel/Clause.hpp"
#include "Kernel/Inference.hpp"
#include "Lib/SharedSet.hpp"
#include "Lib/Int.hpp"

namespace Saturation
{
using namespace std;
using namespace Lib;
using namespace Kernel;

PredicateSplitPassiveClauseContainer::PredicateSplitPassiveClauseContainer(bool isOutermost, const Shell::Options& opt, std::string name,
    std::vector<std::unique_ptr<PassiveClauseContainer>> queues,
    std::vector<float> cutoffs, std::vector<int> ratios, bool layeredArrangement)
  : PassiveClauseContainer(isOutermost, opt, name), _queues(std::move(queues)), _cutoffs(cutoffs), _layeredArrangement(layeredArrangement)
{
  _randomize = opt.randomAWR();

  // sanity checks
  if (ratios.size() != _queues.size()) {
    USER_ERROR("Queue " + name + ": The number of ratios needs to match the number of queues, but " + Int::toString(ratios.size()) + " != " + Int::toString(_queues.size()));
  }
  if (_cutoffs.size() != _queues.size()) {
    USER_ERROR("Queue " + name + ": The number of cutoffs needs to match the number of queues, but " + Int::toString(_cutoffs.size()) + " != " + Int::toString(_queues.size()));
  }

  if (_randomize) {
    _ratioSum = 0;
    for (unsigned i = 0; i < ratios.size(); i++) {
      unsigned ri = ratios[i];
      _ratioSum += ri;
      _ratios.push_back(ri);
    }
  }

  // even when randomizing true selection, we rely on the old ways for simulation:

  // compute lcm, which will be used to compute reverse ratios
  auto lcm = 1;
  for (unsigned i = 0; i < ratios.size(); i++)
  {
    lcm = std::lcm(lcm, ratios[i]);
  }
  // initialize
  for (unsigned i = 0; i < ratios.size(); i++)
  {
    _invertedRatios.push_back(lcm / ratios[i]);
    _balances.push_back(0);
  }
}

PredicateSplitPassiveClauseContainer::~PredicateSplitPassiveClauseContainer() {
}

unsigned PredicateSplitPassiveClauseContainer::bestQueue(float featureValue) const
{
  // compute best queue clause should be placed in
  ASS(_cutoffs.back() == std::numeric_limits<float>::max());
  for (unsigned i = 0; i < _cutoffs.size(); i++)
  {
    if (featureValue <= _cutoffs[i])
    {
      return i;
    }
  }
  // unreachable
  ASSERTION_VIOLATION;
}

void PredicateSplitPassiveClauseContainer::add(Clause* cl)
{
  ASS(cl->store() == Clause::PASSIVE);

  auto bestQueueIndex = bestQueue(evaluateFeature(cl));
  if (_layeredArrangement)
  {
    // add clause to all queues starting from best queue for clause
    for (unsigned i = bestQueueIndex; i < _queues.size(); i++)
    {
      _queues[i]->add(cl);
    }
  }
  else
  {
    // add clause to best queue for clause
    _queues[bestQueueIndex]->add(cl);
  }

  if (_isOutermost)
  {
    addedEvent.fire(cl);
  }

  ASS(cl->store() == Clause::PASSIVE);
}

void PredicateSplitPassiveClauseContainer::remove(Clause* cl)
{
  if (_isOutermost)
  {
    ASS(cl->store()==Clause::PASSIVE);
  }
  auto bestQueueIndex = bestQueue(evaluateFeature(cl));

  if (_layeredArrangement)
  {
    // remove clause from all queues starting from best queue for clause
    for (unsigned i = bestQueueIndex; i < _queues.size(); i++)
    {
      _queues[i]->remove(cl);
    }
  }
  else
  {
    // remove clause from best queue for clause
    _queues[bestQueueIndex]->remove(cl);
  }

  if (_isOutermost)
  {
    ASS(cl->store()==Clause::PASSIVE);
    removedEvent.fire(cl);
    ASS(cl->store() != Clause::PASSIVE);
  }
}

bool PredicateSplitPassiveClauseContainer::isEmpty() const
{ 
  for (const auto& queue : _queues)
  {
    if (!queue->isEmpty())
    {
      return false;
    }
  }
  return true;
}

unsigned PredicateSplitPassiveClauseContainer::sizeEstimate() const
{ 
  ASS(!_queues.empty());

  if (_layeredArrangement)
  {
    // Note: If we use LRS, we lose the invariant that the last queue contains all clauses (since it can have stronger limits than the other queues).
    //       as a consequence the size of the last queue is only an estimate on the size.
    return _queues.back()->sizeEstimate();
  }
  else
  {
    unsigned sum = 0;
    for (const auto& queue : _queues)
    {
      sum += queue->sizeEstimate();
    }
    return sum;
  }
}

Clause* PredicateSplitPassiveClauseContainer::popSelected()
{
  unsigned queueIndex;

  if (_randomize) {
    unsigned toss = Random::getInteger(_ratioSum);
    // cout << "metaqueue " << _name << " toss " << toss << " (below: " << _ratioSum << ")";
    queueIndex = 0;
    while (toss >= _ratios[queueIndex]) {
      toss -= _ratios[queueIndex];
      queueIndex++;
    }
    // cout << " means " << queueIndex << " (below: " << _ratios.size() << ")" << endl;
  } else {
    // compute queue from which we will pick a clause:
    // choose queue using weighted round robin
    auto minElementIt = std::min_element(_balances.begin(), _balances.end());
    auto minElement = *minElementIt; // need to save the value of the min element before updating it to a new value, since it may not remain the minimal element after the update

    queueIndex = std::distance(_balances.begin(), minElementIt);

    _balances[queueIndex] += _invertedRatios[queueIndex];
    for (auto& balance : _balances)
    {
      balance -= minElement;
    }
  }

  // if chosen queue is empty, use the next queue to the right
  // this succeeds in a multi-split-queue-non-LRS-setting where we have the invariant that each clause from queue i is contained in queue j if i<j
  auto currIndex = queueIndex;
  while (currIndex < (long int)_queues.size() && _queues[currIndex]->isEmpty())
  {
    currIndex++;
  }
  // for tammet-style-queues or in the presence of LRS, we need to also consider the queues to the left as additional fallback (using the invar: at least one queue has at least one clause if popSelected is called)
  if (currIndex == (long int)_queues.size())
  {
    // fallback: try remaining queues, at least one of them must be nonempty
    ASS(queueIndex > 0); // otherwise we would already have searched through all queues
    currIndex = queueIndex - 1;
    while (_queues[currIndex]->isEmpty())
    {
      currIndex--;
      ASS(currIndex >= 0);
    }
  }
  ASS(!_queues[currIndex]->isEmpty());

  // pop clause from selected queue
  auto cl = _queues[currIndex]->popSelected();
  ASS(cl->store() == Clause::PASSIVE);

  // note: for a non-layered arrangement, the clause only occurred in _queues[currIndex] (from which it was just removed using popSelected(), so we don't need any additional clause-removal
  if (_layeredArrangement)
  {
    // remove clause from all queues
    for (unsigned i = 0; i < _queues.size(); i++)
    {
      _queues[i]->remove(cl);
    }
  }

  selectedEvent.fire(cl);

  return cl;
}

void PredicateSplitPassiveClauseContainer::simulationInit()
{
  _simulationBalances.clear();
  for (const auto& balance : _balances)
  {
    _simulationBalances.push_back(balance);
  }

  for (const auto& queue : _queues)
  {
    queue->simulationInit();
  }
}

bool PredicateSplitPassiveClauseContainer::simulationHasNext()
{
  bool hasNext = false;
  for (const auto& queue : _queues)
  {
    bool currHasNext = queue->simulationHasNext();
    hasNext = hasNext || currHasNext;
  }
  return hasNext;
}

void PredicateSplitPassiveClauseContainer::simulationPopSelected()
{
  // compute queue from which we will pick a clause:
  // choose queue using weighted round robin
  auto minElementIt = std::min_element(_simulationBalances.begin(), _simulationBalances.end());
  auto minElement = *minElementIt; // need to save the value of the min element before updating it to a new value, since it may not remain the minimal element after the update

  auto queueIndex = std::distance(_simulationBalances.begin(), minElementIt);
  _simulationBalances[queueIndex] += _invertedRatios[queueIndex];
  for (auto& balance : _simulationBalances)
  {
    balance -= minElement;
  }

  // if chosen queue is empty, use the next queue to the right
  //  this succeeds in a multi-split-queue-non-LRS-setting where we have the invariant that each clause from queue i is contained in queue j if i<j
  auto currIndex = queueIndex;
  while (currIndex < (long int)_queues.size() && !_queues[currIndex]->simulationHasNext())
  {
    currIndex++;
  }
  // for tammet-style-queues or in the presence of LRS, we need to also consider the queues to the left as additional fallback (using the invar: at least one queue has at least one clause if popSelected is called)
  if (currIndex == (long int)_queues.size())
  {
    // fallback: try remaining queues, at least one of them must be nonempty
    ASS(queueIndex > 0); // otherwise we would already have searched through all queues
    currIndex = queueIndex - 1;
    while (!_queues[currIndex]->simulationHasNext())
    {
      currIndex--;
      ASS(currIndex >= 0);
    }
  }

  _queues[currIndex]->simulationPopSelected();
}

// returns whether at least one of the limits was tightened
bool PredicateSplitPassiveClauseContainer::setLimitsToMax()
{
  bool tightened = false;
  for (const auto& queue : _queues)
  {
    bool currTightened = queue->setLimitsToMax();
    tightened = tightened || currTightened;
  }
  return tightened;
}

// returns whether at least one of the limits was tightened
bool PredicateSplitPassiveClauseContainer::setLimitsFromSimulation()
{
  bool tightened = false;
  for (const auto& queue : _queues)
  {
    bool currTightened = queue->setLimitsFromSimulation();
    tightened = tightened || currTightened;
  }
  return tightened;
}

void PredicateSplitPassiveClauseContainer::onLimitsUpdated()
{
  for (const auto& queue : _queues)
  {
    queue->onLimitsUpdated();
  }
}

bool PredicateSplitPassiveClauseContainer::mayBeAbleToDiscriminateChildrenOnLimits() const
{
  // just ask the first queue we have
  for (const auto& queue : _queues) return queue->mayBeAbleToDiscriminateChildrenOnLimits();
  return false;
}

bool PredicateSplitPassiveClauseContainer::allChildrenNecessarilyExceedLimits(Clause* cl, unsigned upperBoundNumSelLits) const
{
  // can't conclude any lower bounds on niceness of child-clause, so have to assume that it is potentially added to all queues.
  // In particular we need to check whether at least one of the queues could potentially select children of the clause.
  for (const auto& queue : _queues) {
    if (!queue->allChildrenNecessarilyExceedLimits(cl, upperBoundNumSelLits))
      return false;
  }
  return true;
}

bool PredicateSplitPassiveClauseContainer::mayBeAbleToDiscriminateClausesUnderConstructionOnLimits() const
{
  for (const auto& queue : _queues) {
    if (queue->mayBeAbleToDiscriminateClausesUnderConstructionOnLimits())
      return true;
  }
  return false;
}

bool PredicateSplitPassiveClauseContainer::exceedsAgeLimit(unsigned numPositiveLiterals, const Inference& inference, bool& andThatsIt) const
{
  auto bestQueueIndex = bestQueue(evaluateFeatureEstimate(numPositiveLiterals, inference));
  // note: even for non-layered-arrangements, we need to go through all queues, since the values for age, w, ... are only lower bounds (in the sense that the actual value could lead to a worse bestQueueIndex)
  for (unsigned i = bestQueueIndex; i < _queues.size(); i++) {
    auto& queue = _queues[i];

    if (!queue->exceedsAgeLimit(numPositiveLiterals, inference, andThatsIt))
      return false;
  }
  return true;
}

// returns true if the cl fulfills at least one weight-limit of a queue it is in
// note: w here denotes the weight as returned by weight().
// this method internally takes care of computing the corresponding weightForClauseSelection.

bool PredicateSplitPassiveClauseContainer::exceedsWeightLimit(unsigned w, unsigned numPositiveLiterals, const Inference& inference) const
{
  auto bestQueueIndex = bestQueue(evaluateFeatureEstimate(numPositiveLiterals, inference));
  // note: even for non-layered-arrangements, we need to go through all queues, since the values for age, w, ... are only lower bounds (in the sense that the actual value could lead to a worse bestQueueIndex)
  for (unsigned i = bestQueueIndex; i < _queues.size(); i++)
  {
    auto& queue = _queues[i];
    if (!queue->exceedsWeightLimit(w, numPositiveLiterals, inference))
    {
      return false;
    }
  }
  return true;
}

bool PredicateSplitPassiveClauseContainer::limitsActive() const
{
  for (const auto& queue : _queues) {
    if (queue->limitsActive())
      return true;
  }
  return false;
}

bool PredicateSplitPassiveClauseContainer::exceedsAllLimits(Clause* cl) const
{
  auto bestQueueIndex = bestQueue(evaluateFeature(cl));
  if (_layeredArrangement) {
    // with layered arranegement, all relevant sub-queues should agree that cl exceeds
    for (unsigned i = bestQueueIndex; i < _queues.size(); i++) {
      auto& queue = _queues[i];
      if (!queue->exceedsAllLimits(cl))
        return false;
    }
    return true;
  } else {
    return _queues[bestQueueIndex]->exceedsAllLimits(cl);
  }
}


TheoryMultiSplitPassiveClauseContainer::TheoryMultiSplitPassiveClauseContainer(bool isOutermost, const Shell::Options &opt, std::string name, std::vector<std::unique_ptr<PassiveClauseContainer>> queues) :
PredicateSplitPassiveClauseContainer(isOutermost, opt, name, std::move(queues), opt.theorySplitQueueCutoffs(), opt.theorySplitQueueRatios(), opt.theorySplitQueueLayeredArrangement()) {}

float TheoryMultiSplitPassiveClauseContainer::evaluateFeature(Clause* cl) const
{
  // heuristically compute likeliness that clause occurs in proof
  auto inference = cl->inference();
  auto expectedRatioDenominator = _opt.theorySplitQueueExpectedRatioDenom();
  return inference.th_ancestors * expectedRatioDenominator - inference.all_ancestors;
}

float TheoryMultiSplitPassiveClauseContainer::evaluateFeatureEstimate(unsigned, const Inference& inf) const
{
  // heuristically compute likeliness that clause occurs in proof
  static int expectedRatioDenominator = _opt.theorySplitQueueExpectedRatioDenom();
  return inf.th_ancestors * expectedRatioDenominator - inf.all_ancestors;
}

AvatarMultiSplitPassiveClauseContainer::AvatarMultiSplitPassiveClauseContainer(bool isOutermost, const Shell::Options &opt, std::string name, std::vector<std::unique_ptr<PassiveClauseContainer>> queues) :
PredicateSplitPassiveClauseContainer(isOutermost, opt, name, std::move(queues), opt.avatarSplitQueueCutoffs(), opt.avatarSplitQueueRatios(), opt.avatarSplitQueueLayeredArrangement()) {}

float AvatarMultiSplitPassiveClauseContainer::evaluateFeature(Clause* cl) const
{
  // heuristically compute likeliness that clause occurs in proof
  auto inf = cl->inference();
  return (inf.splits() == nullptr) ? 0 : inf.splits()->size();
}

float AvatarMultiSplitPassiveClauseContainer::evaluateFeatureEstimate(unsigned, const Inference& inf) const
{
  // heuristically compute likeliness that clause occurs in proof
  return (inf.splits() == nullptr) ? 0 : inf.splits()->size();
}

SineLevelMultiSplitPassiveClauseContainer::SineLevelMultiSplitPassiveClauseContainer(bool isOutermost, const Shell::Options &opt, std::string name, std::vector<std::unique_ptr<PassiveClauseContainer>> queues) :
PredicateSplitPassiveClauseContainer(isOutermost, opt, name, std::move(queues), opt.sineLevelSplitQueueCutoffs(), opt.sineLevelSplitQueueRatios(), opt.sineLevelSplitQueueLayeredArrangement()) {}

float SineLevelMultiSplitPassiveClauseContainer::evaluateFeature(Clause* cl) const
{
  // heuristically compute likeliness that clause occurs in proof
  return cl->getSineLevel();
}

float SineLevelMultiSplitPassiveClauseContainer::evaluateFeatureEstimate(unsigned, const Inference& inf) const
{
  // heuristically compute likeliness that clause occurs in proof
  return inf.getSineLevel();
}

PositiveLiteralMultiSplitPassiveClauseContainer::PositiveLiteralMultiSplitPassiveClauseContainer(bool isOutermost, const Shell::Options &opt, std::string name, std::vector<std::unique_ptr<PassiveClauseContainer>> queues) :
PredicateSplitPassiveClauseContainer(isOutermost, opt, name, std::move(queues), opt.positiveLiteralSplitQueueCutoffs(), opt.positiveLiteralSplitQueueRatios(), opt.positiveLiteralSplitQueueLayeredArrangement()) {}

float PositiveLiteralMultiSplitPassiveClauseContainer::evaluateFeature(Clause* cl) const
{
  // heuristically compute likeliness that clause occurs in proof
  return cl->numPositiveLiterals();
}

float PositiveLiteralMultiSplitPassiveClauseContainer::evaluateFeatureEstimate(unsigned numPositiveLiterals, const Inference& inference) const
{
  return numPositiveLiterals;
}

};