docs: @Override
      public synchronized INDArray replicateToDevice(Integer deviceId, INDArray array) {
          if (array == null)
              return null;
  
          // string arrays are stored in host memory only atm
          if (array.isS())
              return array.dup(array.ordering());
  
          if (array.isView())
              throw new UnsupportedOperationException("It's impossible to replicate View");
  
          val shape = array.shape();
          val stride = array.stride();
          val elementWiseStride = array.elementWiseStride();
          val ordering = array.ordering();
          val length = array.length();
          val dtype = array.dataType();
  
          // we use this call to get device memory updated
          AtomicAllocator.getInstance().getPointer(array, (CudaContext) AtomicAllocator.getInstance().getDeviceContext().getContext());
  
          int currentDeviceId = getDeviceForCurrentThread();
  
          if (currentDeviceId != deviceId.intValue()) {
              Nd4j.getMemoryManager().releaseCurrentContext();
              NativeOpsHolder.getInstance().getDeviceNativeOps().setDevice(deviceId);
              attachThreadToDevice(Thread.currentThread().getId(), deviceId);
          }
  
  
          DataBuffer newDataBuffer = replicateToDevice(deviceId, array.data());
          DataBuffer newShapeBuffer = Nd4j.getShapeInfoProvider().createShapeInformation(shape, stride, elementWiseStride, ordering, dtype).getFirst();
          INDArray result = Nd4j.createArrayFromShapeBuffer(newDataBuffer, newShapeBuffer);
  
          if (currentDeviceId != deviceId.intValue()) {
              Nd4j.getMemoryManager().releaseCurrentContext();
              attachThreadToDevice(Thread.currentThread().getId(), currentDeviceId);
              NativeOpsHolder.getInstance().getDeviceNativeOps().setDevice(currentDeviceId);
          }
  
  
          return result;
      }