Repository: huggingface/diffusers

Issue:
unipc schedule throws exception when use_karras_sigmas=True
### Describe the bug


unipc scheduler throws exception when use_karras_sigmas=True

File "/usr/local/src/diffusers/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py", line 642, in __call__
    self.scheduler.set_timesteps(num_inference_steps, device=device)
  File "/usr/local/src/diffusers/src/diffusers/schedulers/scheduling_unipc_multistep.py", line 239, in set_timesteps
    sigmas = self._convert_to_karras(in_sigmas=sigmas, num_inference_steps=num_inference_steps)
  File "/usr/local/src/diffusers/src/diffusers/configuration_utils.py", line 137, in __getattr__
    raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
AttributeError: 'UniPCMultistepScheduler' object has no attribute '_convert_to_karras'


Its just missing the _convert_to_karras and _sigma_to_t functions.
I will submit a pull request shortly


### Reproduction

specify use_karras_sigmas=True when creating unipc scheduler, then use scheduler

### Logs

_No response_

### System Info



- `diffusers` version: 0.19.3
- Platform: Linux-5.10.0-23-amd64-x86_64-with-glibc2.35
- Python version: 3.10.6
- PyTorch version (GPU?): 2.1.0a0+b5021ba (True)
- Huggingface_hub version: 0.16.4
- Transformers version: 4.32.0.dev0
- Accelerate version: 0.22.0.dev0
- xFormers version: 0.0.21+db0e439.d20230810
- Using GPU in script?: yes
- Using distributed or parallel set-up in script?: no


### Who can help?

_No response_


Discussion:

Resolving patch:
diff --git a/src/diffusers/schedulers/scheduling_unipc_multistep.py b/src/diffusers/schedulers/scheduling_unipc_multistep.py
--- a/src/diffusers/schedulers/scheduling_unipc_multistep.py
+++ b/src/diffusers/schedulers/scheduling_unipc_multistep.py
@@ -278,6 +278,44 @@ def _threshold_sample(self, sample: torch.FloatTensor) -> torch.FloatTensor:
 
         return sample
 
+    # Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._sigma_to_t
+    def _sigma_to_t(self, sigma, log_sigmas):
+        # get log sigma
+        log_sigma = np.log(sigma)
+
+        # get distribution
+        dists = log_sigma - log_sigmas[:, np.newaxis]
+
+        # get sigmas range
+        low_idx = np.cumsum((dists >= 0), axis=0).argmax(axis=0).clip(max=log_sigmas.shape[0] - 2)
+        high_idx = low_idx + 1
+
+        low = log_sigmas[low_idx]
+        high = log_sigmas[high_idx]
+
+        # interpolate sigmas
+        w = (low - log_sigma) / (low - high)
+        w = np.clip(w, 0, 1)
+
+        # transform interpolation to time range
+        t = (1 - w) * low_idx + w * high_idx
+        t = t.reshape(sigma.shape)
+        return t
+
+    # Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_karras
+    def _convert_to_karras(self, in_sigmas: torch.FloatTensor, num_inference_steps) -> torch.FloatTensor:
+        """Constructs the noise schedule of Karras et al. (2022)."""
+
+        sigma_min: float = in_sigmas[-1].item()
+        sigma_max: float = in_sigmas[0].item()
+
+        rho = 7.0  # 7.0 is the value used in the paper
+        ramp = np.linspace(0, 1, num_inference_steps)
+        min_inv_rho = sigma_min ** (1 / rho)
+        max_inv_rho = sigma_max ** (1 / rho)
+        sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** rho
+        return sigmas
+
     def convert_model_output(
         self, model_output: torch.FloatTensor, timestep: int, sample: torch.FloatTensor
     ) -> torch.FloatTensor:


Test patch:
diff --git a/tests/schedulers/test_scheduler_unipc.py b/tests/schedulers/test_scheduler_unipc.py
--- a/tests/schedulers/test_scheduler_unipc.py
+++ b/tests/schedulers/test_scheduler_unipc.py
@@ -208,12 +208,24 @@ def test_full_loop_no_noise(self):
 
         assert abs(result_mean.item() - 0.2464) < 1e-3
 
+    def test_full_loop_with_karras(self):
+        sample = self.full_loop(use_karras_sigmas=True)
+        result_mean = torch.mean(torch.abs(sample))
+
+        assert abs(result_mean.item() - 0.2925) < 1e-3
+
     def test_full_loop_with_v_prediction(self):
         sample = self.full_loop(prediction_type="v_prediction")
         result_mean = torch.mean(torch.abs(sample))
 
         assert abs(result_mean.item() - 0.1014) < 1e-3
 
+    def test_full_loop_with_karras_and_v_prediction(self):
+        sample = self.full_loop(prediction_type="v_prediction", use_karras_sigmas=True)
+        result_mean = torch.mean(torch.abs(sample))
+
+        assert abs(result_mean.item() - 0.1966) < 1e-3
+
     def test_fp16_support(self):
         scheduler_class = self.scheduler_classes[0]
         scheduler_config = self.get_scheduler_config(thresholding=True, dynamic_thresholding_ratio=0)