code: def backward(self, dz, ha, hb):
      ha_encoded_t = self.ctx.mpc.cond_call(
          lambda: self.encoder.encode(ha).T,
          lambda: None,
          dst=self.rank_a,
      )
      dz_encoded = self.ctx.mpc.cond_call(
          lambda: self.encoder.encode(dz), lambda: None, dst=self.rank_b
      )
  
      # dh
      enc_wa_share = self.ctx.mpc.option_call(
          lambda: self.phe_cipher.get_tensor_encryptor().encrypt_tensor(
              self.wa.share.T
          ),
          dst=self.rank_a,
      )
      enc_wa_share = self.ctx.mpc.communicator.broadcast(
          enc_wa_share, src=self.rank_a, group=self.group
      )
      enc_dha = self.ctx.mpc.option_call(
          lambda: dz_encoded @ (enc_wa_share + self.wa.share.T),
          dst=self.rank_b,
      )
      enc_dha = self.ctx.mpc.communicator.broadcast(
          enc_dha, src=self.rank_b, group=self.group
      )
      dha = self.ctx.mpc.option_call(
          lambda: self.phe_cipher.get_tensor_decryptor().decrypt_tensor(
              enc_dha
          )
          / self.wa.encoder.scale**2,
          dst=self.rank_a,
      )
      enc_wb_share = self.ctx.mpc.option_call(
          lambda: self.phe_cipher.get_tensor_encryptor().encrypt_tensor(
              self.wb.share.T
          ),
          self.rank_a,
      )
      enc_wb_share = self.ctx.mpc.communicator.broadcast(
          enc_wb_share, src=self.rank_a, group=self.group
      )
      enc_dhb = self.ctx.mpc.option_call(
          lambda: dz_encoded @ enc_wb_share, dst=self.rank_b
      )
      enc_dhb = self.ctx.mpc.communicator.broadcast(
          enc_dhb, src=self.rank_b, group=self.group
      )
      dhb = self.ctx.mpc.option_call(
          lambda: self.phe_cipher.get_tensor_decryptor().decrypt_tensor(
              enc_dhb
          ),
          dst=self.rank_a,
      )
      dhb = self.ctx.mpc.communicator.broadcast(
          dhb, src=self.rank_a, group=self.group
      )
      dhb = self.ctx.mpc.option_call(
          lambda: (dhb + dz_encoded @ self.wb.share.T)
          / self.wb.encoder.scale**2,
          dst=self.rank_b,
      )
      dh = self.ctx.mpc.cond(dha, dhb, dst=self.rank_a)
  
      # update wa
      ga = self.ctx.mpc.sshe.smm(
          self.ctx,
          group=self.group,
          op=torch.matmul,
          rank_1=self.rank_a,
          tensor_1=ha_encoded_t,
          cipher_1=self.phe_cipher,
          rank_2=self.rank_b,
          tensor_2=dz_encoded,
      )
      ga = _rescaler(ga, self.encoder)
  
      # update wb
      gb = self.ctx.mpc.option_call(lambda: hb.T @ dz, dst=self.rank_b)
  
      # set grad for wa and wb
      _set_grad(self.wa, ga)
      _set_grad(self.wb, gb)
  
      return dh
  
imports: import typing
  from typing import Any, Iterator
  import torch
  from torch.nn import Parameter
  from fate.arch.context import Context
  from fate.arch.protocol.mpc.common.encoding import IgnoreEncodings
  from fate.arch.protocol.mpc.mpc import FixedPointEncoder
  from fate.arch.trace import auto_trace
label_desc: Use image node_id 4 for calling the SSHENeuralNetworkAggregator obj's underlying member method code with example usage: obj.backward(dz, ha, hb) and returns: dh