given the code



class GpuAnnealer(SpinGlassSolver):
    @staticmethod
    def precompute_temperature_schedule(initial_temperature, final_temperature, alpha, num_iterations, device):
        temperature_schedule = torch.full((num_iterations,), initial_temperature, device=device)
        decay_steps = min(int(torch.log(torch.tensor(final_temperature / initial_temperature)) / torch.log(torch.tensor(alpha))), num_iterations)
        temperature_schedule[:decay_steps] = initial_temperature * (alpha ** torch.arange(decay_steps, device=device))
        temperature_schedule[decay_steps:] = final_temperature
        return temperature_schedule
    
    def solve(self, instance: SpinGlassBase) -> SpinGlassSolution:
        start_time = time.time()
        initial_temperature = 100.0
        final_temperature = 0.01
        alpha = 0.95
        num_iterations = 1000
        num_runs = 500
        offset_increase_rate = 1
        N = instance.N
        device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

        # Keep the large matrix and spins on the GPU
        J = torch.tensor(instance.couplings, dtype=torch.float32, device=device)  # coupling matrix on GPU
        best_energy = float('inf')
        best_spins = None  # Initialize best spins as None
        
        initial_state = torch.tensor(instance.spins, dtype=torch.float32, device=device)
        # Precompute temperature schedule
        temperature_schedule = self.precompute_temperature_schedule(
            initial_temperature, final_temperature, alpha, num_iterations, device
        )

        # Main loop for multiple runs
        for _ in range(num_runs):
            spins = initial_state.clone()  # Clone initial state on CPU for each run
            temperature = torch.tensor(initial_temperature,dtype=torch.float32)
            Eoffset = torch.tensor(0.0, dtype=torch.float32)  # Offset initialized on CPU
            
            # Monte Carlo steps
            step_time = time.time()
            for step in range(num_iterations):
                temperature = temperature_schedule[step]

                # Calculate energy changes ΔE for flipping each spin
                spins_gpu = spins.to(device)  # Transfer spins to GPU for matrix operations
                delta_Es = 2 * spins_gpu * torch.matmul(J, spins_gpu)  # Compute energy changes on GPU
                delta_Es = delta_Es.cpu()  # Move result back to CPU
                
                # Adjust ΔE with the offset and decide on flips (on CPU)
                accept_probabilities = torch.exp(-(delta_Es - Eoffset) / temperature)
                accepted_flips = (delta_Es < Eoffset) | (torch.rand(N) < accept_probabilities)

                if torch.any(accepted_flips):
                    # If at least one flip is accepted, randomly select one and apply it
                    accepted_indices = torch.nonzero(accepted_flips).squeeze()
                    if accepted_indices.dim() == 0:
                        # Only one accepted flip, directly use it
                        selected_flip = accepted_indices.item()
                    else:
                        # Multiple accepted flips, choose one randomly
                        selected_flip = accepted_indices[torch.randint(len(accepted_indices), (1,)).item()]
                    spins[selected_flip] *= -1  # Apply the selected flip on CPU
                    Eoffset = 0.0  # Reset the offset on CPU
                else:
                    # Increase the offset if no flip is accepted
                    Eoffset += offset_increase_rate  # Offset updated on CPU
                
            print("for each montecarlo it takes",  time.time()- step_time)
            # Calculate the final energy for this run
            spins_gpu = spins.to(device)  # Transfer spins to GPU for energy calculation
            final_energy = -0.5 * torch.sum(spins_gpu * torch.matmul(J, spins_gpu)).item()  # Fully GPU computation

            if final_energy < best_energy:
                best_energy = final_energy  # Update best energy on CPU
                best_spins = spins.clone()  # Update best spins on CPU

        solving_time = time.time() - start_time

        # Return final result
        final_spins = best_spins.numpy()  # Final spins are already on CPU
        return SpinGlassSolution(final_spins, best_energy, solving_time)



i get in  accept_probabilities = torch.exp(-(delta_Es - Eoffset) / temperature)
RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!
why can you fix