title: "Enforcement literal not supported in constraint" with condition on addExactlyOne() in OR-Tools
body: <p>This is a follow-up to <a href="https://stackoverflow.com/questions/71962117/impose-constraint-on-cpmodel-only-when-some-relation-between-intvars-is-true">Impose Constraint on CpModel only when some relation between IntVars is true</a>, using OR-Tools in Java. Following the answer in that question and referring to <a href="https://github.com/google/or-tools/blob/stable/ortools/sat/docs/channeling.md" rel="nofollow noreferrer">this OR-Tools page on channeling constraints</a>, the rendered model is Invalid and can't be solved.</p>
  <p>The main code snippet causing the issue is:</p>
  <pre><code>// iterating over int t,j; workers is a List&lt;Literal&gt;.
  
  //Channel constraint:
  Literal timeIsAfterStepStart = model.newBoolVar(&quot;time_&quot;+t+&quot;after_step_&quot;+j+&quot;_start&quot;);
  
  //Implement timeIsAfterStepStart == (step.start &lt;= t)
  model.addLessOrEqual(jobVariable.start, t).onlyEnforceIf(timeIsAfterStepStart);
  model.addGreaterThan(jobVariable.start, t).onlyEnforceIf(timeIsAfterStepStart.not());
                  
  //including this line causes INVALID MODEL status
  model.addExactlyOne(workers).onlyEnforceIf(timeIsAfterStepStart);
  </code></pre>
  <p>When using <code>model.addExactlyOne()</code>, the solver returns an Invalid Model response:</p>
  <pre><code>Starting CP-SAT solver v9.3.10497
  Parameters: log_search_progress: true
  Setting number of workers to 4
  Invalid model: Enforcement literal not supported in constraint: enforcement_literal: 24 exactly_one { literals: 0 }
  </code></pre>
  <p>However, if I replace that line with the dummy code</p>
  <pre><code>//This works totally fine
  IntVar dummy = model.newIntVar(0, 10, &quot;dummy&quot;+t+&quot;&quot;+j);
  model.addEquality(dummy, 5).onlyEnforceIf(timeIsAfterStepStart);
  model.addEquality(dummy, 0).onlyEnforceIf(timeIsAfterStepStart.not());
  </code></pre>
  <p>which suggests the channeling logic is fine, and that the error has something to do with addExactlyOne(). What is causing the Invalid Model error?</p>
  <p><strong>Full reproducible example:</strong></p>
  <pre><code>import com.google.ortools.Loader;
  import com.google.ortools.sat.*;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  public class ReproducibleInvalidModelExample {
      public static void main(String[] args) {
          Loader.loadNativeLibraries();
          class Job {
              int duration;
              String name;
              Job(int duration, String name){
                  this.duration = duration;
                  this.name = name;
              }
          }
  
          class JobVariable {
              IntVar start, end;
              IntervalVar duration;
          }
  
          final Job[] jobs = new Job[]{new Job(4, &quot;A&quot;), new Job(6, &quot;B&quot;)};
          final int timeUpperBound = 10;
          final int numJobs = 2;
          final int numWorkers = 1;
  
          CpModel model = new CpModel();
  
          //Initialize assignments array.
          Literal[][][] assignments = new Literal[numWorkers][numJobs][timeUpperBound];
          for(int w = 0; w &lt; numWorkers; w++){
              for(int j = 0; j &lt; numJobs; j++){
                  for(int t = 0; t &lt; timeUpperBound; t++){
                      assignments[w][j][t] = model.newBoolVar(&quot;Worker&quot;+w+&quot;Job&quot;+j+&quot;Time&quot;+t);
                  }
              }
          }
  
          //Initialize IntVars and IntervalVar for each job
          Map&lt;Job, JobVariable&gt; jobToVariable = new HashMap&lt;&gt;();
          for(Job job : jobs){
              JobVariable jobVar = new JobVariable();
              jobVar.start = model.newIntVar(0, timeUpperBound, &quot;start:&quot; + job.name);
              jobVar.end = model.newIntVar(0, timeUpperBound, &quot;end:&quot; + job.name);
              jobVar.duration = model.newIntervalVar(jobVar.start,
                      LinearExpr.constant(job.duration), jobVar.end, &quot;interval:&quot; + job.name);
              jobToVariable.put(job, jobVar);
          }
  
  
          //Each Job has One Worker at a Time, while each Job is Active:
          for(int j = 0; j &lt; numJobs; j++){
              JobVariable jobVariable = jobToVariable.get(jobs[j]);
              for(int t = 0; t &lt; timeUpperBound; t++){
                  List&lt;Literal&gt; workers = new ArrayList&lt;&gt;();
                  for(int w = 0; w &lt; numWorkers; w++){
                      workers.add(assignments[w][j][t]);
                  }
  
                  //Channel constraint:
                  Literal timeIsAfterStepStart = model.newBoolVar(&quot;time_&quot;+t+&quot;after_step_&quot;+j+&quot;_start&quot;);
  
                  //Implement timeIsAfterStepStart == (step.start &lt;= t)
                  model.addLessOrEqual(jobVariable.start, t).onlyEnforceIf(timeIsAfterStepStart);
                  model.addGreaterThan(jobVariable.start, t).onlyEnforceIf(timeIsAfterStepStart.not());
  
                  //including this line causes INVALID MODEL status
                  model.addExactlyOne(workers).onlyEnforceIf(timeIsAfterStepStart);
  
                  //This works totally fine
                  IntVar dummy = model.newIntVar(0, 10, &quot;dummy&quot;+t+&quot;&quot;+j);
                  model.addEquality(dummy, 5).onlyEnforceIf(timeIsAfterStepStart);
                  model.addEquality(dummy, 0).onlyEnforceIf(timeIsAfterStepStart.not());
              }
          }
  
          CpSolver solver = new CpSolver();
          CpSolverSolutionCallback solCallback = new CpSolverSolutionCallback();
          solver.getParameters().setLogSearchProgress(true);
          CpSolverStatus status = solver.solve(model, solCallback);
      }
  }
  </code></pre>
  <p><strong>Edit</strong>: I did some digging, and noticed that <code>addExactlyOne()</code> was added in the most recent (March '22) update to OR-Tools: maybe this is a bug in the new release, that this new method doesn't yet support constraints? See <a href="https://developers.google.com/optimization/support/release_notes" rel="nofollow noreferrer">the release notes here.</a></p>