Repository: stan-dev/cmdstanpy

Issue:
allow iter_warmup when adapt_engaged = False
#### Summary:

In the `sample()` method of a model, if I set `adapt_engaged = False` and `iter_warmup = 1000`,  I get this message:

```
ValueError: Argument "adapt_engaged" is False, cannot specify warmup iterations.
```

We want to allow burnin even without adapting tuning parameters.  So we should be able to turn off adaptation and have a non-zero number of warmup iterations.

#### Current Version:
1.20


Discussion:

Patch:
diff --git a/cmdstanpy/cmdstan_args.py b/cmdstanpy/cmdstan_args.py
index 984d696..0054fdc 100644
--- a/cmdstanpy/cmdstan_args.py
+++ b/cmdstanpy/cmdstan_args.py
@@ -139,10 +139,9 @@ class SamplerArgs:
                     'Value for iter_warmup must be a non-negative integer,'
                     ' found {}.'.format(self.iter_warmup)
                 )
-            if self.iter_warmup > 0 and not self.adapt_engaged:
+            if self.iter_warmup == 0 and self.adapt_engaged:
                 raise ValueError(
-                    'Argument "adapt_engaged" is False, '
-                    'cannot specify warmup iterations.'
+                    'Must specify iter_warmup > 0 when adapt_engaged=True.'
                 )
         if self.iter_sampling is not None:
             if self.iter_sampling < 0 or not isinstance(


Test patch:
diff --git a/test/test_cmdstan_args.py b/test/test_cmdstan_args.py
index d1ee8aa..7587564 100644
--- a/test/test_cmdstan_args.py
+++ b/test/test_cmdstan_args.py
@@ -111,7 +111,7 @@ def test_bad() -> None:
     with pytest.raises(ValueError):
         args.validate(chains=2)
 
-    args = SamplerArgs(iter_warmup=10, adapt_engaged=False)
+    args = SamplerArgs(iter_warmup=0, adapt_engaged=True)
     with pytest.raises(ValueError):
         args.validate(chains=2)
 
diff --git a/test/test_sample.py b/test/test_sample.py
index 02404d5..62f8831 100644
--- a/test/test_sample.py
+++ b/test/test_sample.py
@@ -1435,6 +1435,26 @@ def test_dont_save_warmup(caplog: pytest.LogCaptureFixture) -> None:
     )
 
 
+def test_warmup_no_adapt() -> None:
+    # we may want to have a "burn-in" period, even without adaptation
+    stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan')
+    jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json')
+
+    bern_model = CmdStanModel(stan_file=stan)
+    bern_fit = bern_model.sample(
+        data=jdata,
+        chains=2,
+        seed=12345,
+        iter_warmup=200,
+        iter_sampling=100,
+        adapt_engaged=False,
+    )
+
+    assert bern_fit.column_names == tuple(BERNOULLI_COLS)
+    assert bern_fit.num_draws_sampling == 100
+    assert bern_fit.draws().shape == (100, 2, len(BERNOULLI_COLS))
+
+
 def test_sampler_diags() -> None:
     stan = os.path.join(DATAFILES_PATH, 'bernoulli.stan')
     jdata = os.path.join(DATAFILES_PATH, 'bernoulli.data.json')