docstr: Securely computes tensor_3 = op(tensor_1, tensor_2), where:
      1. tensor_1 is a tensor that belongs to rank_1
      2. tensor_2 is a tensor that belongs to rank_2.
      3. tensor_3 is a mpc tensor that belongs to rank_1 and rank_2.
  
  the cipher is provided by rank_1 and is used to encrypt tensor_1.
  One should choose the smaller tensor to be encrypted by PHE to improve performance.
docstr_tok: ["Securely","computes","tensor_3","=","op","(","tensor_1",",","tensor_2",")",",","where",":","1.","tensor_1","is","a","tensor","that","belongs","to","rank_1","2.","tensor_2","is","a","tensor","that","belongs","to","rank_2",".","3.","tensor_3","is","a","mpc","tensor","that","belongs","to","rank_1","and","rank_2",".","the","cipher","is","provided","by","rank_1","and","is","used","to","encrypt","tensor_1",".","One","should","choose","the","smaller","tensor","to","be","encrypted","by","PHE","to","improve","performance","."]
code: def smm(
      cls,
      ctx: Context,
      group,
      op: typing.Callable[[torch.Tensor, torch.Tensor], torch.Tensor],
      rank_1,
      tensor_1,
      cipher_1,
      rank_2,
      tensor_2,
  ) -> ArithmeticSharedTensor:
      """
      Securely computes tensor_3 = op(tensor_1, tensor_2), where:
          1. tensor_1 is a tensor that belongs to rank_1
          2. tensor_2 is a tensor that belongs to rank_2.
          3. tensor_3 is a mpc tensor that belongs to rank_1 and rank_2.
  
      the cipher is provided by rank_1 and is used to encrypt tensor_1.
      One should choose the smaller tensor to be encrypted by PHE to improve performance.
      """
      from fate.arch.context import PHECipher
  
      assert (
          isinstance(rank_1, int)
          and isinstance(rank_2, int)
          and 0 <= rank_1 != rank_2 >= 0
      ), "invalid rank_a or rank_b"
      ctx.mpc.option_assert(
          lambda: tensor_1 is not None,
          "tensor_1 should not be None on rank_1",
          dst=rank_1,
      )
      ctx.mpc.option_assert(
          lambda: tensor_1 is None,
          "tensor_1 should not be None on rank_2",
          dst=rank_2,
      )
      ctx.mpc.option_assert(
          lambda: tensor_2 is not None,
          "tensor_2 should not be None on rank_2",
          dst=rank_2,
      )
      ctx.mpc.option_assert(
          lambda: tensor_2 is None,
          "tensor_2 should be None on rank_1",
          dst=rank_1,
      )
      ctx.mpc.option_assert(
          lambda: isinstance(cipher_1, PHECipher),
          "invalid cipher",
          dst=rank_1,
      )
  
      tensor_1_enc = ctx.mpc.communicator.broadcast_obj(
          obj=ctx.mpc.option_call(
              lambda: cipher_1.get_tensor_encryptor().encrypt_tensor(
                  tensor_1
              ),
              dst=rank_1,
          ),
          src=rank_1,
          group=group,
      )
      return cls.phe_to_mpc(
          ctx,
          group,
          rank_a=rank_2,
          rank_b=rank_1,
          phe_cipher_b=ctx.mpc.option(cipher_1, dst=rank_1),
          phe_tensor_a=ctx.mpc.option_call(
              lambda: op(tensor_1_enc, tensor_2), dst=rank_2
          ),
      )
  
file_path: FATE/python/fate/arch/protocol/mpc/primitives/sshe.py
imports: import typing
  import torch
  from fate.arch.context import Context
  from fate.arch.tensor import DTensor
  from fate.arch.protocol.mpc.common.rng import generate_random_ring_element
  from fate.arch.protocol.mpc.primitives.arithmetic import ArithmeticSharedTensor
  from fate.arch.protocol.mpc.primitives.beaver import IgnoreEncodings
  from fate.arch.trace import auto_trace
label_desc: Use image node_id 2 for calling the SSHE obj's underlying member method code with example usage: obj.smm(cls, ctx, group, op, rank_1, tensor_1, cipher_1, rank_2, tensor_2) and returns: cls