LCOV - code coverage report
Current view: top level - buildbot/coverage/build/src/main - portfolio_driver.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 217 531 40.9 %
Date: 2026-03-16 10:41:14 Functions: 16 34 47.1 %
Branches: 97 280 34.6 %

           Branch data     Line data    Source code
       1                 :            : /******************************************************************************
       2                 :            :  * This file is part of the cvc5 project.
       3                 :            :  *
       4                 :            :  * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
       5                 :            :  * in the top-level source directory and their institutional affiliations.
       6                 :            :  * All rights reserved.  See the file COPYING in the top-level source
       7                 :            :  * directory for licensing information.
       8                 :            :  * ****************************************************************************
       9                 :            :  *
      10                 :            :  */
      11                 :            : #include "main/portfolio_driver.h"
      12                 :            : 
      13                 :            : #if HAVE_SYS_WAIT_H
      14                 :            : #include <signal.h>
      15                 :            : #include <sys/wait.h>
      16                 :            : #include <unistd.h>
      17                 :            : #endif
      18                 :            : 
      19                 :            : #include <cvc5/cvc5.h>
      20                 :            : 
      21                 :            : #include <chrono>
      22                 :            : #include <cstdlib>
      23                 :            : #include <optional>
      24                 :            : #include <thread>
      25                 :            : 
      26                 :            : #include "base/check.h"
      27                 :            : #include "base/exception.h"
      28                 :            : #include "base/output.h"
      29                 :            : #include "main/command_executor.h"
      30                 :            : #include "parser/commands.h"
      31                 :            : #include "parser/command_status.h"
      32                 :            : 
      33                 :            : using namespace cvc5::parser;
      34                 :            : 
      35                 :            : namespace cvc5::main {
      36                 :            : 
      37                 :            : enum SolveStatus : int
      38                 :            : {
      39                 :            :   STATUS_SOLVED = 0,
      40                 :            :   STATUS_UNSOLVED = 1,
      41                 :            : };
      42                 :            : 
      43                 :      23902 : bool ExecutionContext::solveContinuous(parser::InputParser* parser,
      44                 :            :                                        bool stopAtSetLogic,
      45                 :            :                                        bool stopAtCheckSat)
      46                 :            : {
      47                 :      23902 :   Command cmd;
      48                 :      23902 :   bool status = true;
      49         [ +  + ]:     688946 :   while (status)
      50                 :            :   {
      51                 :     688863 :     cmd = parser->nextCommand();
      52         [ +  + ]:     688805 :     if (cmd.isNull())
      53                 :            :     {
      54                 :      20726 :       break;
      55                 :            :     }
      56                 :     668079 :     Cmd* cc = cmd.d_cmd.get();
      57         [ +  + ]:     668079 :     if (stopAtCheckSat)
      58                 :            :     {
      59 [ +  - ][ +  + ]:          4 :       if (dynamic_cast<CheckSatCommand*>(cc) != nullptr)
      60                 :            :       {
      61                 :          1 :         d_hasReadCheckSat = true;
      62                 :          1 :         break;
      63                 :            :       }
      64                 :            :     }
      65                 :     668078 :     status = d_executor->doCommand(&cmd);
      66 [ +  + ][ -  + ]:     668076 :     if (!status && cc->interrupted())
                 [ -  + ]
      67                 :            :     {
      68                 :          0 :       solver().getDriverOptions().out() << CommandInterrupted();
      69                 :          0 :       d_executor->reset();
      70                 :          0 :       break;
      71                 :            :     }
      72 [ +  - ][ +  + ]:     668076 :     if (dynamic_cast<QuitCommand*>(cc) != nullptr)
      73                 :            :     {
      74                 :       3031 :       break;
      75                 :            :     }
      76         [ +  + ]:     665045 :     if (stopAtSetLogic)
      77                 :            :     {
      78         [ +  - ]:          1 :       auto* slc = dynamic_cast<SetBenchmarkLogicCommand*>(cc);
      79         [ +  - ]:          1 :       if (slc != nullptr)
      80                 :            :       {
      81                 :          1 :         d_logic = slc->getLogic();
      82                 :          1 :         break;
      83                 :            :       }
      84                 :            :     }
      85                 :            :   }
      86                 :      23842 :   return status;
      87                 :      23902 : }
      88                 :            : 
      89                 :          0 : bool ExecutionContext::continueAfterSolving(parser::InputParser* parser)
      90                 :            : {
      91                 :          0 :   Command cmd;
      92                 :          0 :   bool status = true;
      93         [ -  - ]:          0 :   while (status)
      94                 :            :   {
      95                 :          0 :     cmd = parser->nextCommand();
      96         [ -  - ]:          0 :     if (cmd.isNull())
      97                 :            :     {
      98                 :          0 :       break;
      99                 :            :     }
     100                 :          0 :     Cmd* cc = cmd.d_cmd.get();
     101 [ -  - ][ -  - ]:          0 :     if (dynamic_cast<GetModelCommand*>(cc) != nullptr)
     102                 :            :     {
     103                 :            :       try
     104                 :            :       {
     105                 :          0 :         std::string result = solver().getModel(d_sorts, d_terms);
     106                 :          0 :         solver().getDriverOptions().out() << result;
     107                 :          0 :         status = true;
     108                 :          0 :       }
     109         [ -  - ]:          0 :       catch (std::exception& e)
     110                 :            :       {
     111                 :          0 :         status = false;
     112                 :          0 :       }
     113                 :            :     }
     114 [ -  - ][ -  - ]:          0 :     else if (dynamic_cast<GetUnsatCoreCommand*>(cc) != nullptr)
     115                 :            :     {
     116                 :            :       try
     117                 :            :       {
     118                 :          0 :         std::vector<cvc5::Term> core = solver().getUnsatCore();
     119                 :          0 :         std::ostream& out = solver().getDriverOptions().out();
     120                 :          0 :         out << "(" << std::endl;
     121         [ -  - ]:          0 :         for (const cvc5::Term& t : core)
     122                 :            :         {
     123                 :          0 :           auto it = d_named_terms.find(t);
     124         [ -  - ]:          0 :           if (it != d_named_terms.end())
     125                 :            :           {
     126                 :          0 :             out << it->second << std::endl;
     127                 :            :           }
     128                 :            :         }
     129                 :          0 :         out << ")" << std::endl;
     130                 :          0 :         status = true;
     131                 :          0 :       }
     132         [ -  - ]:          0 :       catch (std::exception& e)
     133                 :            :       {
     134                 :          0 :         status = false;
     135                 :          0 :       }
     136                 :            :     }
     137                 :            :     else
     138                 :            :     {
     139                 :          0 :       status = d_executor->doCommand(&cmd);
     140                 :            :     }
     141                 :          0 :     if (!status && cc->interrupted())
     142                 :            :     {
     143                 :          0 :       solver().getDriverOptions().out() << CommandInterrupted();
     144                 :          0 :       d_executor->reset();
     145                 :          0 :       break;
     146                 :            :     }
     147 [ -  - ][ -  - ]:          0 :     if (dynamic_cast<QuitCommand*>(cc) != nullptr)
     148                 :            :     {
     149                 :          0 :       break;
     150                 :            :     }
     151                 :            :   }
     152                 :          0 :   return status;
     153                 :          0 : }
     154                 :            : 
     155                 :          0 : bool ExecutionContext::runCheckSatCommand()
     156                 :            : {
     157                 :          0 :   std::shared_ptr<Cmd> cmd(new CheckSatCommand());
     158                 :          0 :   Command command(cmd);
     159                 :          0 :   return d_executor->doCommand(&command);
     160                 :          0 : }
     161                 :            : 
     162                 :          0 : void ExecutionContext::storeDeclarationsAndNamedTerms()
     163                 :            : {
     164                 :          0 :   SymbolManager* sm = d_executor->getSymbolManager();
     165                 :          0 :   d_sorts = sm->getDeclaredSorts();
     166                 :          0 :   d_terms = sm->getDeclaredTerms();
     167                 :          0 :   d_named_terms = sm->getNamedTerms();
     168                 :          0 : }
     169                 :            : 
     170                 :          0 : bool ExecutionContext::runResetCommand()
     171                 :            : {
     172                 :          0 :   std::shared_ptr<Cmd> cmd(new ResetCommand());
     173                 :          0 :   Command command(cmd);
     174                 :          0 :   return d_executor->doCommand(&command);
     175                 :          0 : }
     176                 :            : 
     177                 :          0 : std::vector<Command> ExecutionContext::parseCommands(
     178                 :            :     parser::InputParser* parser)
     179                 :            : {
     180                 :          0 :   std::vector<Command> res;
     181                 :            :   while (true)
     182                 :            :   {
     183                 :          0 :     Command cmd = parser->nextCommand();
     184         [ -  - ]:          0 :     if (cmd.isNull())
     185                 :            :     {
     186                 :          0 :       break;
     187                 :            :     }
     188                 :          0 :     res.emplace_back(cmd);
     189 [ -  - ][ -  - ]:          0 :     if (dynamic_cast<QuitCommand*>(cmd.d_cmd.get()) != nullptr)
     190                 :            :     {
     191                 :          0 :       break;
     192                 :            :     }
     193         [ -  - ]:          0 :   }
     194                 :          0 :   return res;
     195                 :          0 : }
     196                 :            : 
     197                 :          0 : bool ExecutionContext::solveCommands(std::vector<Command>& cmds)
     198                 :            : {
     199                 :          0 :   bool status = true;
     200         [ -  - ]:          0 :   for (Command& cmd : cmds)
     201                 :            :   {
     202                 :          0 :     status = d_executor->doCommand(&cmd);
     203                 :          0 :     Cmd* cc = cmd.d_cmd.get();
     204                 :          0 :     if (!status && cc->interrupted())
     205                 :            :     {
     206                 :          0 :       solver().getDriverOptions().out() << CommandInterrupted();
     207                 :          0 :       d_executor->reset();
     208                 :          0 :       break;
     209                 :            :     }
     210                 :            : 
     211 [ -  - ][ -  - ]:          0 :     if (dynamic_cast<QuitCommand*>(cc) != nullptr)
     212                 :            :     {
     213                 :          0 :       break;
     214                 :            :     }
     215                 :            :   }
     216                 :          0 :   return status;
     217                 :            : }
     218                 :            : 
     219                 :            : #if HAVE_SYS_WAIT_H
     220                 :            : 
     221                 :            : namespace {
     222                 :            : 
     223                 :            : /**
     224                 :            :  * Provides a convenient wrapper for POSIX pipes in the context of forking.
     225                 :            :  * The implemented mechanism is using a pipe to buffer the (standard or error)
     226                 :            :  * output of a child process and optionally copy it to the respective output of
     227                 :            :  * the parent process. This wrapper closely follows
     228                 :            :  * http://www.microhowto.info/howto/capture_the_output_of_a_child_process_in_c.html
     229                 :            :  */
     230                 :            : class Pipe
     231                 :            : {
     232                 :            :  public:
     233                 :            :   /** Open a new pipe */
     234                 :          2 :   void open()
     235                 :            :   {
     236         [ -  + ]:          2 :     if (pipe(d_pipe) == -1)
     237                 :            :     {
     238                 :          0 :       throw internal::Exception("Unable to open pipe for child process");
     239                 :            :     }
     240                 :          2 :   }
     241                 :            :   /**
     242                 :            :    * Redirects the given file descriptor fd into this pipe using dup2 and closes
     243                 :            :    * both ends of the pipe. This method should be called within the child
     244                 :            :    * process after forking to redirect standard out or error out into the pipe.
     245                 :            :    */
     246                 :          0 :   void dup(int fd)
     247                 :            :   {
     248                 :            :     // dup2 may get interrupted by a signal. If this happens the error is EINTR
     249                 :            :     // and we can simply try again.
     250 [ -  - ][ -  - ]:          0 :     while ((dup2(d_pipe[1], fd) == -1) && (errno == EINTR))
                 [ -  - ]
     251                 :            :     {
     252                 :            :     }
     253                 :          0 :     close(d_pipe[0]);
     254                 :          0 :     close(d_pipe[1]);
     255                 :          0 :   }
     256                 :            :   /**
     257                 :            :    * Close the input of this pipe. This method should be called within the
     258                 :            :    * parent process after forking.
     259                 :            :    */
     260                 :          2 :   void closeIn() { close(d_pipe[1]); }
     261                 :            :   /**
     262                 :            :    * Copy the content of the pipe into the given output stream. This method
     263                 :            :    * should be called within the parent process after the child process has
     264                 :            :    * terminated.
     265                 :            :    */
     266                 :          3 :   void flushTo(std::ostream& os)
     267                 :            :   {
     268                 :            :     char buf[4096];
     269                 :            :     while (true)
     270                 :            :     {
     271                 :          3 :       ssize_t cnt = read(d_pipe[0], buf, sizeof(buf));
     272         [ -  + ]:          3 :       if (cnt == -1)
     273                 :            :       {
     274         [ -  - ]:          0 :         if (errno == EINTR)
     275                 :            :         {
     276                 :          0 :           continue;
     277                 :            :         }
     278                 :            :         else
     279                 :            :         {
     280                 :          0 :           throw internal::Exception("Unable to read from pipe");
     281                 :            :         }
     282                 :            :       }
     283         [ +  + ]:          3 :       else if (cnt == 0)
     284                 :            :       {
     285                 :          2 :         break;
     286                 :            :       }
     287                 :          1 :       os.write(buf, cnt);
     288                 :          1 :     }
     289                 :          2 :   }
     290                 :            : 
     291                 :            :  private:
     292                 :            :   int d_pipe[2];
     293                 :            : };
     294                 :            : 
     295                 :          1 : void printPortfolioConfig(Solver& solver, PortfolioConfig& config)
     296                 :            : {
     297                 :          1 :   bool dry_run = solver.getOption("portfolio-dry-run") == "true";
     298                 :          1 :   if (dry_run || solver.isOutputOn("portfolio"))
     299                 :            :   {
     300         [ -  + ]:          1 :     std::ostream& out = (dry_run) ? solver.getDriverOptions().out()
     301                 :          1 :                                   : solver.getOutput("portfolio");
     302                 :          1 :     out << "(portfolio \"" << config.toOptionString() << "\"";
     303                 :          1 :     out << " :timeout " << config.d_timeout;
     304                 :          1 :     out << ")" << std::endl;
     305                 :            :   }
     306                 :          1 : }
     307                 :            : 
     308                 :            : /**
     309                 :            :  * Manages running portfolio configurations until one has solved the input
     310                 :            :  * problem. Depending on --portfolio-jobs runs multiple jobs in parallel.
     311                 :            :  */
     312                 :            : class PortfolioProcessPool
     313                 :            : {
     314                 :            :   enum class JobState
     315                 :            :   {
     316                 :            :     PENDING,
     317                 :            :     RUNNING,
     318                 :            :     DONE
     319                 :            :   };
     320                 :            :   /**
     321                 :            :    * A job, consisting of the configuration, the pids of the worker and timeout
     322                 :            :    * process, the stderr and stdout pipes and the job state.
     323                 :            :    * Initially, a job is created but not started and all properties except for
     324                 :            :    * the configuration have their default value. Then starting a job, the state
     325                 :            :    * ich changed to RUNNING and the pids and pipes have their proper values. If
     326                 :            :    * no timeout is enforced, the timeout pid remains unchanged. After the job
     327                 :            :    * has finished, checkResults() eventually analyzes the jobs result and
     328                 :            :    * changes the state to DONE.
     329                 :            :    */
     330                 :            :   struct Job
     331                 :            :   {
     332                 :         25 :     Job(PortfolioConfig config)
     333                 :         25 :         : d_config(config),
     334                 :         25 :           d_worker(-1),
     335                 :         25 :           d_timeout(-1),
     336         [ +  + ]:         50 :           d_errPipe(),
     337         [ +  + ]:         50 :           d_outPipe(),
     338                 :         25 :           d_state(JobState::PENDING)
     339                 :            :     {
     340                 :         25 :     }
     341                 :            :     PortfolioConfig d_config;
     342                 :            :     pid_t d_worker;
     343                 :            :     pid_t d_timeout;
     344                 :            :     Pipe d_errPipe;
     345                 :            :     Pipe d_outPipe;
     346                 :            :     JobState d_state;
     347                 :            :   };
     348                 :            : 
     349                 :            :  public:
     350                 :          1 :   PortfolioProcessPool(ExecutionContext& ctx, parser::InputParser* parser, uint64_t timeout)
     351                 :          1 :       : d_ctx(ctx),
     352                 :          1 :         d_parser(parser),
     353                 :          1 :         d_maxJobs(ctx.solver().getOptionInfo("portfolio-jobs").uintValue()),
     354                 :          1 :         d_timeout(timeout)
     355                 :            :   {
     356                 :          1 :   }
     357                 :            : 
     358                 :          1 :   bool run(PortfolioStrategy& strategy)
     359                 :            :   {
     360         [ +  + ]:         26 :     for (const auto& s : strategy.d_strategies)
     361                 :            :     {
     362                 :         25 :       d_jobs.emplace_back(Job{s});
     363                 :            :     }
     364                 :            : 
     365                 :            :     // While there are jobs to be run or jobs still running
     366 [ -  + ][ -  - ]:          1 :     while (d_nextJob < d_jobs.size() || d_running > 0)
                 [ +  - ]
     367                 :            :     {
     368                 :            :       // Check if any job was successful
     369         [ -  + ]:          1 :       if (checkResults())
     370                 :            :       {
     371                 :          0 :         return true;
     372                 :            :       }
     373                 :            : 
     374                 :            :       // While we can start jobs right now
     375 [ +  - ][ +  + ]:          2 :       while (d_nextJob < d_jobs.size() && d_running < d_maxJobs)
                 [ +  + ]
     376                 :            :       {
     377                 :          1 :         startNextJob();
     378                 :            :       }
     379                 :            : 
     380         [ +  - ]:          1 :       if (d_running > 0)
     381                 :            :       {
     382                 :          1 :         int wstatus = 0;
     383                 :          1 :         pid_t child = wait(&wstatus);
     384         [ +  - ]:          1 :         if (checkResults(child, wstatus))
     385                 :            :         {
     386                 :          1 :           return true;
     387                 :            :         }
     388                 :            :       }
     389                 :            :     }
     390         [ -  - ]:          0 :     if (checkResults()) return true;
     391                 :            : 
     392                 :          0 :     return false;
     393                 :            :   }
     394                 :            : 
     395                 :            :  private:
     396                 :          1 :   void startNextJob()
     397                 :            :   {
     398 [ -  + ][ -  + ]:          1 :     Assert(d_nextJob < d_jobs.size());
                 [ -  - ]
     399                 :          1 :     Job& job = d_jobs[d_nextJob];
     400         [ +  - ]:          1 :     Trace("portfolio") << "Starting " << job.d_config << std::endl;
     401                 :          1 :     printPortfolioConfig(d_ctx.solver(), job.d_config);
     402                 :            : 
     403                 :            :     // Set up pipes to capture output of worker
     404                 :          1 :     job.d_errPipe.open();
     405                 :          1 :     job.d_outPipe.open();
     406                 :            :     // Start the worker process
     407                 :          1 :     job.d_worker = fork();
     408         [ -  + ]:          1 :     if (job.d_worker == -1)
     409                 :            :     {
     410                 :          0 :       throw internal::Exception("Unable to fork");
     411                 :            :     }
     412         [ -  + ]:          1 :     if (job.d_worker == 0)
     413                 :            :     {
     414                 :          0 :       job.d_errPipe.dup(STDERR_FILENO);
     415                 :          0 :       job.d_outPipe.dup(STDOUT_FILENO);
     416                 :            : 
     417                 :          0 :       std::vector<cvc5::Term> assertions = d_ctx.solver().getAssertions();
     418                 :          0 :       std::string logic = d_ctx.solver().getLogic();
     419                 :            : 
     420                 :            :       std::string produceUnsatCoresValue =
     421                 :          0 :           d_ctx.solver().getOption("produce-unsat-cores");
     422                 :            :       std::string produceModelsValue =
     423                 :          0 :           d_ctx.solver().getOption("produce-models");
     424                 :          0 :       d_ctx.storeDeclarationsAndNamedTerms();
     425                 :            : 
     426                 :          0 :       d_ctx.runResetCommand();
     427                 :            : 
     428                 :          0 :       d_ctx.solver().setOption("produce-unsat-cores", produceUnsatCoresValue);
     429                 :          0 :       d_ctx.solver().setOption("produce-models", produceModelsValue);
     430                 :          0 :       job.d_config.applyOptions(d_ctx.solver());
     431                 :          0 :       d_ctx.solver().setLogic(logic);
     432                 :            : 
     433         [ -  - ]:          0 :       for (Term& t : assertions)
     434                 :            :       {
     435                 :          0 :         d_ctx.solver().assertFormula(t);
     436                 :            :       }
     437                 :            :       // 0 = solved, 1 = not solved
     438                 :          0 :       SolveStatus rc = SolveStatus::STATUS_UNSOLVED;
     439         [ -  - ]:          0 :       if (d_ctx.runCheckSatCommand())
     440                 :            :       // if (d_ctx.solveCommands(d_commands))
     441                 :            :       {
     442                 :          0 :         Result res = d_ctx.d_executor->getResult();
     443                 :          0 :         d_ctx.continueAfterSolving(d_parser);
     444                 :          0 :         if (res.isSat() || res.isUnsat())
     445                 :            :         {
     446                 :          0 :           rc = SolveStatus::STATUS_SOLVED;
     447                 :            :         }
     448                 :          0 :       }
     449                 :          0 :       _exit(rc);
     450                 :          0 :     }
     451                 :          1 :     job.d_errPipe.closeIn();
     452                 :          1 :     job.d_outPipe.closeIn();
     453                 :            : 
     454                 :            :     // Start the timeout process
     455 [ +  - ][ +  - ]:          1 :     if (d_timeout > 0 && job.d_config.d_timeout > 0)
     456                 :            :     {
     457                 :          1 :       job.d_timeout = fork();
     458         [ -  + ]:          1 :       if (job.d_timeout == 0)
     459                 :            :       {
     460                 :            :         auto duration = std::chrono::duration<double, std::milli>(
     461                 :          0 :             job.d_config.d_timeout * d_timeout);
     462                 :          0 :         std::this_thread::sleep_for(duration);
     463                 :          0 :         kill(job.d_worker, SIGKILL);
     464                 :          0 :         _exit(0);
     465                 :            :       }
     466                 :            :     }
     467                 :            : 
     468                 :          1 :     ++d_nextJob;
     469                 :          1 :     ++d_running;
     470                 :          1 :     job.d_state = JobState::RUNNING;
     471                 :          1 :   }
     472                 :            : 
     473                 :            :   /**
     474                 :            :    * Check whether some process terminated and solved the input. If so,
     475                 :            :    * forward the child process output to the main out and return true.
     476                 :            :    * Otherwise return false.
     477                 :            :    * If child and status are given, only the job with this particular worker is
     478                 :            :    * considered and status is assumed to be the wstatus reported by waitpid.
     479                 :            :    */
     480                 :          2 :   bool checkResults(pid_t child = -1, int status = 0)
     481                 :            :   {
     482                 :            :     // check d_jobs for items where worker has terminated and timeout != -1
     483         [ +  + ]:         27 :     for (auto& job : d_jobs)
     484                 :            :     {
     485                 :            :       // has not been started yet
     486         [ +  + ]:         26 :       if (job.d_state == JobState::PENDING) continue;
     487                 :            :       // has already been analyzed
     488         [ -  + ]:          1 :       if (job.d_state == JobState::DONE) continue;
     489                 :            :       // was given an explicit child, but this is not it.
     490 [ +  - ][ -  + ]:          1 :       if (child != -1 && job.d_worker != child) continue;
     491                 :            : 
     492                 :          1 :       int wstatus = 0;
     493         [ -  + ]:          1 :       if (child == -1)
     494                 :            :       {
     495                 :          0 :         pid_t res = waitpid(job.d_worker, &wstatus, WNOHANG);
     496                 :            :         // has not terminated yet
     497         [ -  - ]:          0 :         if (res == 0) continue;
     498         [ -  - ]:          0 :         if (res == -1) continue;
     499                 :            :       }
     500                 :            :       else
     501                 :            :       {
     502                 :          1 :         wstatus = status;
     503                 :            :       }
     504                 :            :       // mark as analyzed
     505         [ +  - ]:          1 :       Trace("portfolio") << "Finished " << job.d_config << std::endl;
     506                 :            :       // terminate the corresponding timeout process if it is still running
     507         [ +  - ]:          1 :       if (job.d_timeout > 0)
     508                 :            :       {
     509                 :          1 :         kill(job.d_timeout, SIGKILL);
     510                 :            :       }
     511                 :          1 :       job.d_state = JobState::DONE;
     512                 :          1 :       --d_running;
     513                 :            :       // check if exited normally
     514         [ -  + ]:          1 :       if (WIFSIGNALED(wstatus))
     515                 :            :       {
     516                 :          0 :         continue;
     517                 :            :       }
     518         [ +  - ]:          1 :       if (WIFEXITED(wstatus))
     519                 :            :       {
     520         [ +  - ]:          1 :         if (WEXITSTATUS(wstatus) == SolveStatus::STATUS_SOLVED)
     521                 :            :         {
     522         [ +  - ]:          1 :           Trace("portfolio") << "Successful!" << std::endl;
     523         [ +  - ]:          1 :           if (d_ctx.solver().isOutputOn("portfolio"))
     524                 :            :           {
     525                 :          1 :             std::ostream& out = d_ctx.solver().getOutput("portfolio");
     526                 :          1 :             out << "(portfolio-success \"" << job.d_config.toOptionString()
     527                 :          1 :                 << "\")" << std::endl;
     528                 :            :           }
     529                 :          1 :           job.d_errPipe.flushTo(std::cerr);
     530                 :          1 :           job.d_outPipe.flushTo(std::cout);
     531                 :          1 :           return true;
     532                 :            :         }
     533                 :            :       }
     534                 :            :     }
     535                 :          1 :     return false;
     536                 :            :   }
     537                 :            : 
     538                 :            :   ExecutionContext& d_ctx;
     539                 :            :   parser::InputParser* d_parser;
     540                 :            :   /** All jobs. */
     541                 :            :   std::vector<Job> d_jobs;
     542                 :            :   /** The id of the next job to be started within d_jobs */
     543                 :            :   size_t d_nextJob = 0;
     544                 :            :   /** The number of currently running jobs */
     545                 :            :   size_t d_running = 0;
     546                 :            :   const uint64_t d_maxJobs;
     547                 :            :   const uint64_t d_timeout;
     548                 :            : };
     549                 :            : 
     550                 :            : }  // namespace
     551                 :            : 
     552                 :            : #endif
     553                 :            : 
     554                 :      23901 : bool PortfolioDriver::solve(std::unique_ptr<CommandExecutor>& executor)
     555                 :            : {
     556                 :      23901 :   ExecutionContext ctx{executor.get()};
     557                 :      23901 :   Solver& solver = ctx.solver();
     558                 :      23901 :   bool use_portfolio = solver.getOption("use-portfolio") == "true";
     559         [ +  + ]:      23901 :   if (!use_portfolio)
     560                 :            :   {
     561                 :      23900 :     return ctx.solveContinuous(d_parser, false);
     562                 :            :   }
     563                 :            : #if HAVE_SYS_WAIT_H
     564                 :          1 :   ctx.solveContinuous(d_parser, true);
     565                 :            : 
     566         [ -  + ]:          1 :   if (!ctx.d_logic)
     567                 :            :   {
     568                 :          0 :     return ctx.solveContinuous(d_parser, false);
     569                 :            :   }
     570                 :            : 
     571                 :          1 :   bool dry_run = solver.getOption("portfolio-dry-run") == "true";
     572                 :            : 
     573                 :          1 :   bool incremental_solving = solver.getOption("incremental") == "true";
     574                 :          1 :   PortfolioStrategy strategy = getStrategy(incremental_solving, *ctx.d_logic);
     575 [ -  + ][ -  + ]:          1 :   Assert(!strategy.d_strategies.empty()) << "The portfolio strategy should never be empty.";
                 [ -  - ]
     576         [ -  + ]:          1 :   if (strategy.d_strategies.size() == 1)
     577                 :            :   {
     578                 :          0 :     PortfolioConfig& config = strategy.d_strategies.front();
     579                 :          0 :     printPortfolioConfig(ctx.solver(), config);
     580         [ -  - ]:          0 :     if (dry_run) return true;
     581                 :          0 :     config.applyOptions(solver);
     582                 :          0 :     return ctx.solveContinuous(d_parser, false);
     583                 :            :   }
     584                 :            : 
     585                 :          1 :   uint64_t total_timeout = ctx.solver().getOptionInfo("tlimit").uintValue();
     586         [ +  - ]:          1 :   if (total_timeout == 0)
     587                 :            :   {
     588                 :          1 :     total_timeout = 1'200'000; // miliseconds
     589                 :            :   }
     590                 :            : 
     591         [ -  + ]:          1 :   if (dry_run)
     592                 :            :   {
     593         [ -  - ]:          0 :     for (PortfolioConfig& config : strategy.d_strategies)
     594                 :            :     {
     595                 :          0 :       printPortfolioConfig(ctx.solver(), config);
     596                 :            :     }
     597                 :          0 :     return true;
     598                 :            :   }
     599                 :            : 
     600                 :          1 :   bool uninterrupted = ctx.solveContinuous(d_parser, false, true);
     601 [ +  - ][ +  - ]:          1 :   if (uninterrupted && ctx.d_hasReadCheckSat)
     602                 :            :   {
     603                 :          1 :     PortfolioProcessPool pool(ctx, d_parser, total_timeout);  // ctx.parseCommands(d_parser));
     604                 :          1 :     bool solved = pool.run(strategy);
     605         [ -  + ]:          1 :     if (!solved)
     606                 :            :     {
     607                 :          0 :       std::cout << "unknown" << std::endl;
     608                 :            :     }
     609                 :          1 :     return solved;
     610                 :          1 :   }
     611                 :          0 :   return uninterrupted;
     612                 :            : #else
     613                 :            :   Warning() << "Can't run portfolio without <sys/wait.h>.";
     614                 :            :   return ctx.solveContinuous(d_parser, false);
     615                 :            : #endif
     616                 :      23901 : }
     617                 :            : 
     618                 :          2 : std::string PortfolioConfig::toOptionString() const
     619                 :            : {
     620                 :          2 :   std::stringstream ss;
     621                 :          2 :   bool firstTime = true;
     622         [ +  + ]:          6 :   for (const std::pair<std::string, std::string>& o : d_options)
     623                 :            :   {
     624         [ +  + ]:          4 :     if (firstTime)
     625                 :            :     {
     626                 :          2 :       firstTime = false;
     627                 :            :     }
     628                 :            :     else
     629                 :            :     {
     630                 :          2 :       ss << " ";
     631                 :            :     }
     632                 :          4 :     ss << "--";
     633         [ +  + ]:          4 :     if (o.second == "true")
     634                 :            :     {
     635                 :          2 :       ss << o.first;
     636                 :            :     }
     637         [ -  + ]:          2 :     else if (o.second == "false")
     638                 :            :     {
     639                 :          0 :       ss << "no-" << o.first;
     640                 :            :     }
     641                 :            :     else
     642                 :            :     {
     643                 :          2 :       ss << o.first << "=" << o.second;
     644                 :            :     }
     645                 :            :   }
     646                 :          4 :   return ss.str();
     647                 :          2 : }
     648                 :            : 
     649                 :          0 : std::ostream& operator<<(std::ostream& os, const PortfolioConfig& config)
     650                 :            : {
     651         [ -  - ]:          0 :   for (const auto& o : config.d_options)
     652                 :            :   {
     653                 :          0 :     os << o.first << "=" << o.second << " ";
     654                 :            :   }
     655                 :          0 :   os << "timeout=" << config.d_timeout;
     656                 :          0 :   return os;
     657                 :            : }
     658                 :            : 
     659                 :            : /**
     660                 :            :  * Check if the first string (the logic) is one of the remaining strings.
     661                 :            :  * Used to have a reasonably concise syntax to check the current logic against a
     662                 :            :  * lengthy list.
     663                 :            :  */
     664                 :            : template <typename... T>
     665                 :          5 : bool isOneOf(const std::string& logic, T&&... list)
     666                 :            : {
     667 [ +  - ][ +  - ]:          5 :   return ((logic == list) || ...);
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ -  + ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     668                 :            : }
     669                 :            : 
     670                 :          1 : PortfolioStrategy PortfolioDriver::getStrategy(bool incremental_solving,
     671                 :            :                                                const std::string& logic)
     672                 :            : {
     673         [ -  + ]:          1 :   if (incremental_solving)
     674                 :            :   {
     675                 :          0 :     return getIncrementalStrategy(logic);
     676                 :            :   }
     677                 :            :   else
     678                 :            :   {
     679                 :          1 :     return getNonIncrementalStrategy(logic);
     680                 :            :   }
     681                 :            : }
     682                 :            : 
     683                 :          0 : PortfolioStrategy PortfolioDriver::getIncrementalStrategy(
     684                 :            :     const std::string& logic)
     685                 :            : {
     686                 :          0 :   PortfolioStrategy s;
     687         [ -  - ]:          0 :   if (isOneOf(logic, "QF_AUFLIA"))
     688                 :            :   {
     689                 :          0 :     s.add().unset("arrays-eager-index").set("arrays-eager-lemmas");
     690                 :            :   }
     691         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_BV"))
     692                 :            :   {
     693                 :          0 :     s.add().set("bitblast", "eager");
     694                 :            :   }
     695                 :            :   else
     696                 :            :   {
     697                 :          0 :     s.add();
     698                 :            :   }
     699                 :          0 :   return s;
     700                 :          0 : }
     701                 :            : 
     702                 :          1 : PortfolioStrategy PortfolioDriver::getNonIncrementalStrategy(
     703                 :            :     const std::string& logic)
     704                 :            : {
     705                 :          1 :   PortfolioStrategy s;
     706         [ -  + ]:          1 :   if (isOneOf(logic, "QF_LRA"))
     707                 :            :   {
     708                 :          0 :     s.add(0.166666667)
     709                 :          0 :         .set("miplib-trick")
     710                 :          0 :         .set("miplib-trick-subs", "4")
     711                 :          0 :         .set("use-approx")
     712                 :          0 :         .set("lemmas-on-replay-failure")
     713                 :          0 :         .set("replay-early-close-depth", "4")
     714                 :          0 :         .set("replay-lemma-reject-cut", "128")
     715                 :          0 :         .set("replay-reject-cut", "512")
     716                 :          0 :         .set("unconstrained-simp")
     717                 :          0 :         .set("use-soi");
     718                 :          0 :     s.add(0.583333333)
     719                 :          0 :         .unset("restrict-pivots")
     720                 :          0 :         .set("use-soi")
     721                 :          0 :         .set("new-prop")
     722                 :          0 :         .set("unconstrained-simp");
     723                 :          0 :     s.add();
     724                 :            :   }
     725         [ -  + ]:          1 :   else if (isOneOf(logic, "QF_LIA"))
     726                 :            :   {
     727                 :            :     // same as QF_LRA but add --pb-rewrites
     728                 :          0 :     s.add(0.95)
     729                 :          0 :         .set("miplib-trick")
     730                 :          0 :         .set("miplib-trick-subs", "4")
     731                 :          0 :         .set("use-approx")
     732                 :          0 :         .set("lemmas-on-replay-failure")
     733                 :          0 :         .set("replay-early-close-depth", "4")
     734                 :          0 :         .set("replay-lemma-reject-cut", "128")
     735                 :          0 :         .set("replay-reject-cut", "512")
     736                 :          0 :         .set("unconstrained-simp")
     737                 :          0 :         .set("use-soi")
     738                 :          0 :         .set("pb-rewrites")
     739                 :          0 :         .set("ite-simp")
     740                 :          0 :         .set("simp-ite-compress");
     741                 :          0 :     s.add();
     742                 :            :   }
     743         [ -  + ]:          1 :   else if (isOneOf(logic, "QF_NIA"))
     744                 :            :   {
     745                 :          0 :     s.add(0.35)
     746                 :          0 :         .set("nl-ext-tplanes")
     747                 :          0 :         .set("decision", "justification");
     748                 :          0 :     s.add(0.05)
     749                 :          0 :         .set("nl-ext-tplanes")
     750                 :          0 :         .set("decision", "internal");
     751                 :          0 :     s.add(0.05)
     752                 :          0 :         .unset("nl-ext-tplanes")
     753                 :          0 :         .set("decision", "internal");
     754                 :          0 :     s.add(0.05)
     755                 :          0 :         .unset("arith-brab")
     756                 :          0 :         .set("nl-ext-tplanes")
     757                 :          0 :         .set("decision", "internal");
     758                 :            :     // totals to more than 100%, but smaller bit-widths usually fail quickly
     759                 :          0 :     s.add(0.25)
     760                 :          0 :         .set("solve-int-as-bv", "2")
     761                 :          0 :         .set("bitblast", "eager");
     762                 :          0 :     s.add(0.25)
     763                 :          0 :         .set("solve-int-as-bv", "4")
     764                 :          0 :         .set("bitblast", "eager");
     765                 :          0 :     s.add(0.25)
     766                 :          0 :         .set("solve-int-as-bv", "8")
     767                 :          0 :         .set("bitblast", "eager");
     768                 :          0 :     s.add(0.25)
     769                 :          0 :         .set("solve-int-as-bv", "16")
     770                 :          0 :         .set("bitblast", "eager");
     771                 :          0 :     s.add(0.5)
     772                 :          0 :         .set("solve-int-as-bv", "32")
     773                 :          0 :         .set("bitblast", "eager");
     774                 :          0 :     s.add().set("nl-ext-tplanes").set("decision", "internal");
     775                 :            :   }
     776         [ -  + ]:          1 :   else if (isOneOf(logic, "QF_NRA"))
     777                 :            :   {
     778                 :          0 :     s.add(0.5).set("decision", "justification");
     779                 :          0 :     s.add(0.25)
     780                 :          0 :         .set("decision", "internal")
     781                 :          0 :         .unset("nl-cov")
     782                 :          0 :         .set("nl-ext", "full")
     783                 :          0 :         .set("nl-ext-tplanes");
     784                 :          0 :     s.add().set("decision", "internal").set("nl-ext", "none");
     785                 :            :   }
     786         [ +  - ]:          1 :   else if (isOneOf(logic,
     787                 :            :                    "ALIA",
     788                 :            :                    "AUFLIA",
     789                 :            :                    "AUFLIRA",
     790                 :            :                    "AUFNIRA",
     791                 :            :                    "UF",
     792                 :            :                    "UFBVLIA",
     793                 :            :                    "UFBVFP",
     794                 :            :                    "UFIDL",
     795                 :            :                    "UFLIA",
     796                 :            :                    "UFLRA",
     797                 :            :                    "UFNIA",
     798                 :            :                    "UFDT",
     799                 :            :                    "UFDTLIA",
     800                 :            :                    "UFDTLIRA",
     801                 :            :                    "AUFDTLIA",
     802                 :            :                    "AUFDTLIRA",
     803                 :            :                    "AUFBV",
     804                 :            :                    "AUFBVDTLIA",
     805                 :            :                    "AUFBVFP",
     806                 :            :                    "AUFNIA",
     807                 :            :                    "UFFPDTLIRA",
     808                 :            :                    "UFFPDTNIRA"))
     809                 :            :   {
     810                 :            :     // initial runs
     811                 :          1 :     s.add(0.025).set("simplification", "none").set("enum-inst");
     812                 :          1 :     s.add(0.025).unset("e-matching").set("enum-inst");
     813                 :          1 :     s.add(0.025)
     814                 :          2 :         .unset("e-matching")
     815                 :          2 :         .set("enum-inst")
     816                 :          1 :         .set("enum-inst-sum");
     817                 :            :     // trigger selections
     818                 :          1 :     s.add(0.025).set("relevant-triggers").set("enum-inst");
     819                 :          1 :     s.add(0.025).set("trigger-sel", "max").set("enum-inst");
     820                 :          1 :     s.add(0.025)
     821                 :          2 :         .set("multi-trigger-when-single")
     822                 :          1 :         .set("enum-inst");
     823                 :          1 :     s.add(0.025)
     824                 :          2 :         .set("multi-trigger-when-single")
     825                 :          2 :         .set("multi-trigger-priority")
     826                 :          1 :         .set("enum-inst");
     827                 :          1 :     s.add(0.025).set("multi-trigger-cache").set("enum-inst");
     828                 :          1 :     s.add(0.025).unset("multi-trigger-linear").set("enum-inst");
     829                 :            :     // other
     830                 :          1 :     s.add(0.025).set("pre-skolem-quant", "on").set("enum-inst");
     831                 :          1 :     s.add(0.025).set("inst-when", "full").set("enum-inst");
     832                 :          1 :     s.add(0.025)
     833                 :          2 :         .unset("e-matching")
     834                 :          2 :         .unset("cbqi")
     835                 :          1 :         .set("enum-inst");
     836                 :          1 :     s.add(0.025).set("enum-inst").set("quant-ind");
     837                 :          1 :     s.add(0.025)
     838                 :          2 :         .set("decision", "internal")
     839                 :          2 :         .set("simplification", "none")
     840                 :          2 :         .unset("inst-no-entail")
     841                 :          2 :         .unset("cbqi")
     842                 :          1 :         .set("enum-inst");
     843                 :          1 :     s.add(0.025)
     844                 :          2 :         .set("decision", "internal")
     845                 :          2 :         .set("enum-inst")
     846                 :          1 :         .set("enum-inst-sum");
     847                 :          1 :     s.add(0.025).set("term-db-mode", "relevant").set("enum-inst");
     848                 :          1 :     s.add(0.025).set("enum-inst-interleave").set("enum-inst");
     849                 :          1 :     s.add(0.025).set("preregister-mode", "lazy").set("enum-inst");
     850                 :            :     // finite model find
     851                 :          1 :     s.add(0.025).set("finite-model-find").set("fmf-mbqi", "none");
     852                 :          1 :     s.add(0.025)
     853                 :          2 :         .set("finite-model-find")
     854                 :          1 :         .set("decision", "internal");
     855                 :          1 :     s.add(0.025)
     856                 :          2 :         .set("finite-model-find")
     857                 :          2 :         .set("macros-quant")
     858                 :          1 :         .set("macros-quant-mode", "all");
     859                 :          1 :     s.add(0.05).set("finite-model-find").set("e-matching");
     860                 :          1 :     s.add(0.05).set("mbqi");
     861                 :            :     // long runs
     862                 :          1 :     s.add(0.15)
     863                 :          2 :         .set("finite-model-find")
     864                 :          1 :         .set("decision", "internal");
     865                 :          1 :     s.add().set("enum-inst");
     866                 :            :   }
     867         [ -  - ]:          0 :   else if (isOneOf(logic, "UFBV"))
     868                 :            :   {
     869                 :            :     // most problems in UFBV are essentially BV
     870                 :          0 :     s.add(0.125).set("sygus-inst");
     871                 :          0 :     s.add(0.125).set("mbqi").unset("cegqi").unset("sygus-inst");
     872                 :          0 :     s.add(0.25)
     873                 :          0 :         .set("enum-inst")
     874                 :          0 :         .set("cegqi-nested-qe")
     875                 :          0 :         .set("decision", "internal");
     876                 :          0 :     s.add(0.25)
     877                 :          0 :         .set("mbqi-enum")
     878                 :          0 :         .unset("cegqi")
     879                 :          0 :         .unset("sygus-inst");
     880                 :          0 :     s.add(0.025)
     881                 :          0 :         .set("enum-inst")
     882                 :          0 :         .unset("cegqi-innermost")
     883                 :          0 :         .set("global-negate");
     884                 :            :     ;
     885                 :          0 :     s.add().set("finite-model-find");
     886                 :            :   }
     887         [ -  - ]:          0 :   else if (isOneOf(logic, "ABV", "BV"))
     888                 :            :   {
     889                 :          0 :     s.add(0.066666667).set("sygus-inst");
     890                 :          0 :     s.add(0.066666667).set("mbqi").unset("cegqi").unset("sygus-inst");
     891                 :          0 :     s.add(0.25)
     892                 :          0 :         .set("mbqi-enum")
     893                 :          0 :         .unset("cegqi")
     894                 :          0 :         .unset("sygus-inst");
     895                 :          0 :     s.add(0.25)
     896                 :          0 :         .set("enum-inst")
     897                 :          0 :         .set("cegqi-nested-qe")
     898                 :          0 :         .set("decision", "internal");
     899                 :          0 :     s.add(0.025).set("enum-inst").unset("cegqi-bv");
     900                 :          0 :     s.add(0.025)
     901                 :          0 :         .set("enum-inst")
     902                 :          0 :         .set("cegqi-bv-ineq", "eq-slack");
     903                 :          0 :     s.add(0.066666667).set("enum-inst").unset("cegqi-innermost").set("global-negate");
     904                 :          0 :     s.add().set("enum-inst");
     905                 :            :   }
     906         [ -  - ]:          0 :   else if (isOneOf(logic, "ABVFP", "ABVFPLRA", "BVFP", "FP", "NIA", "NRA", "BVFPLRA"))
     907                 :            :   {
     908                 :          0 :     s.add(0.25)
     909                 :          0 :         .set("mbqi-enum")
     910                 :          0 :         .unset("cegqi")
     911                 :          0 :         .unset("sygus-inst");
     912                 :          0 :     s.add(0.25).set("enum-inst").set("nl-ext-tplanes");
     913                 :          0 :     s.add(0.05).set("mbqi").unset("cegqi").unset("sygus-inst");
     914                 :          0 :     s.add().set("sygus-inst");
     915                 :            :   }
     916         [ -  - ]:          0 :   else if (isOneOf(logic, "LIA", "LRA"))
     917                 :            :   {
     918                 :          0 :     s.add(0.025).set("enum-inst");
     919                 :          0 :     s.add(0.25).set("enum-inst").set("cegqi-nested-qe");
     920                 :          0 :     s.add(0.025).set("mbqi").unset("cegqi").unset("sygus-inst");
     921                 :          0 :     s.add(0.025)
     922                 :          0 :         .set("mbqi-enum")
     923                 :          0 :         .unset("cegqi")
     924                 :          0 :         .unset("sygus-inst");
     925                 :          0 :     s.add()
     926                 :          0 :         .set("enum-inst")
     927                 :          0 :         .set("cegqi-nested-qe")
     928                 :          0 :         .set("decision", "internal");
     929                 :            :   }
     930         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_AUFBV"))
     931                 :            :   {
     932                 :          0 :     s.add(0.5);
     933                 :          0 :     s.add().set("decision", "stoponly");
     934                 :            :   }
     935         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_ABV"))
     936                 :            :   {
     937                 :          0 :     s.add(0.41666667)
     938                 :          0 :         .set("ite-simp")
     939                 :          0 :         .set("simp-with-care")
     940                 :          0 :         .set("repeat-simp");
     941                 :          0 :     s.add();
     942                 :            :   }
     943         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_BV"))
     944                 :            :   {
     945                 :          0 :     s.add().set("bitblast", "eager").set("bv-assert-input");
     946                 :            :   }
     947         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_UFBV"))
     948                 :            :   {
     949                 :          0 :     s.add(0.75).set("bitblast", "eager").set("bv-assert-input");
     950                 :          0 :     s.add();
     951                 :            :   }
     952         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_AUFLIA"))
     953                 :            :   {
     954                 :          0 :     s.add()
     955                 :          0 :         .unset("arrays-eager-index")
     956                 :          0 :         .set("arrays-eager-lemmas")
     957                 :          0 :         .set("decision", "justification");
     958                 :            :   }
     959         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_AX"))
     960                 :            :   {
     961                 :          0 :     s.add()
     962                 :          0 :         .unset("arrays-eager-index")
     963                 :          0 :         .set("arrays-eager-lemmas")
     964                 :          0 :         .set("decision", "internal");
     965                 :            :   }
     966         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_AUFNIA"))
     967                 :            :   {
     968                 :          0 :     s.add()
     969                 :          0 :         .set("decision", "justification")
     970                 :          0 :         .unset("arrays-eager-index")
     971                 :          0 :         .set("arrays-eager-lemmas");
     972                 :            :   }
     973         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_ALIA"))
     974                 :            :   {
     975                 :          0 :     s.add(0.116666667).set("decision", "justification");
     976                 :          0 :     s.add()
     977                 :          0 :         .set("decision", "stoponly")
     978                 :          0 :         .unset("arrays-eager-index")
     979                 :          0 :         .set("arrays-eager-lemmas");
     980                 :            :   }
     981         [ -  - ]:          0 :   else if (isOneOf(logic, "QF_S", "QF_SLIA"))
     982                 :            :   {
     983                 :          0 :     s.add(0.25)
     984                 :          0 :         .set("strings-exp")
     985                 :          0 :         .set("strings-fmf")
     986                 :          0 :         .unset("jh-rlv-order");
     987                 :          0 :     s.add().set("strings-exp").unset("jh-rlv-order");
     988                 :            :   }
     989                 :            :   else
     990                 :            :   {
     991                 :          0 :     s.add();
     992                 :            :   }
     993                 :          1 :   return s;
     994                 :          0 : }
     995                 :            : 
     996                 :            : }  // namespace cvc5::main

Generated by: LCOV version 1.14