text: import  torch
  from    torch import nn
  from    torch.nn import functional as F
  from    torch.utils.data import TensorDataset, DataLoader
  from    torch import optim
  import  numpy as np
  
  from    complexLearner2 import Learner
  from    copy import deepcopy
  #import xlrd
  import xlwt
  
  class Meta(nn.Module):
      """
      Meta Learner
      """
      def __init__(self, args, config):
          """
  
          :param args:
          """
          super(Meta, self).__init__()
  
          self.update_lr = args.update_lr
          self.meta_lr = args.meta_lr
          self.n_way = args.n_way
          self.k_spt = args.k_spt
          self.k_qry = args.k_qry
          self.task_num = args.task_num
          self.update_step = args.update_step
          self.update_step_test = args.update_step_test
  
  
          self.net = Learner(config, args.imgc, args.imgsz).to(torch.device('cuda'))#self.net就是metaLearner这个module
          self.meta_optim = optim.Adam(self.net.parameters(), lr=self.meta_lr)#meta-level outer learning rate
  
  
  
  
      def clip_grad_by_norm_(self, grad, max_norm):
          """
          in-place gradient clipping.就地梯度裁剪 by norm
          :param grad: list of gradients
          :param max_norm: maximum norm allowable
          :return:
          """
  
          total_norm = 0
          counter = 0
          for g in grad:
              param_norm = g.data.norm(2)#y=v:其实就是对y张量L2范数，先对y中每一项取平方，之后累加，最后取根号
              total_norm += param_norm.item() ** 2
              counter += 1
          total_norm = total_norm ** (1. / 2)
  
          clip_coef = max_norm / (total_norm + 1e-6)
          if clip_coef < 1:
              for g in grad:
                  g.data.mul_(clip_coef)
  
          return total_norm/counter
  
  
      def forward(self, x_spt, y_spt, x_qry, y_qry):
          """
  
          :param x_spt:   [b, setsz, c_, h, w]
          :param y_spt:   [b, setsz]
          :param x_qry:   [b, querysz, c_, h, w]
          :param y_qry:   [b, querysz]
          :return:
          """
          #x_spt=x_spt.type(torch.complex64)
          #x_qry=x_qry.type(torch.complex64)
          #print('!!!!!!!!!!')
          #print("x_spt_AFTER: ", x_spt[0][0][0][0][0][0])
          #print(x_spt.size())
          #print(x_spt.type())
          task_num, setsz, c_, h, w = x_spt.size()
          #x_spt=x_spt.permute(5,1,2,3,4,0)#分GPU后遗症
          #x_spt=x_spt.contiguous().view(num2,setsz,c_,h,w)
          #print(x_spt.size())
          #print(x_qry.size())
          querysz = x_qry.size(1)
  
          losses_q = [0 for _ in range(self.update_step + 1)]  # losses_q[i] is the loss on step i 第i步的损失
          corrects = [0 for _ in range(self.update_step + 1)]
  
  
          for i in range(task_num):
  
              # 1. run the i-th task and compute loss for k=0
              logits = self.net(x_spt[i], vars=None, bn_training=True).to(torch.device('cuda'))#前向传播预测的值 net是complexLearner(config) 复数形式 出来的结果是abs()
              #print(logits.type())
              #print(y_spt[i].type())
              loss = F.cross_entropy(logits, y_spt[i].long())#实数域
              #print(loss.type())
              #print(self.net.parameters())
              grad = torch.autograd.grad(loss, self.net.parameters())#outputs, inputs
              fast_weights = list(map(lambda p: p[1] - self.update_lr * p[0], zip(grad, self.net.parameters())))
  
              # this is the loss and accuracy before first update
              with torch.no_grad():
                  # [setsz, nway]
                  logits_q = self.net(x_qry[i], self.net.parameters(), bn_training=True)#用net parameters
                  loss_q = F.cross_entropy(logits_q, y_qry[i].long())
                  losses_q[0] += loss_q
  
                  pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)
                  correct = torch.eq(pred_q, y_qry[i]).sum().item()
                  corrects[0] = corrects[0] + correct#计算准确率
  
              # this is the loss and accuracy after the first update 用fastweights
              with torch.no_grad():
                  # [setsz, nway]
                  logits_q = self.net(x_qry[i], fast_weights, bn_training=True)
                  loss_q = F.cross_entropy(logits_q, y_qry[i])#预测值和真实值计算交叉熵
                  losses_q[1] += loss_q
                  # [setsz]
                  pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)#激活函数
                  correct = torch.eq(pred_q, y_qry[i]).sum().item()
                  corrects[1] = corrects[1] + correct
  
              for k in range(1, self.update_step):
                  # 1. run the i-th task and compute loss for k=1~K-1
                  logits = self.net(x_spt[i], fast_weights, bn_training=True)#vars从None到fastweights
                  loss = F.cross_entropy(logits, y_spt[i])
                  # 2. compute grad on theta_pi
                  grad = torch.autograd.grad(loss, fast_weights)
                  # 3. theta_pi = theta_pi - train_lr * grad
                  fast_weights = list(map(lambda p: p[1] - self.update_lr * p[0], zip(grad, fast_weights)))
  
                  logits_q = self.net(x_qry[i], fast_weights, bn_training=True)
                  # loss_q will be overwritten and just keep the loss_q on last update step.
                  loss_q = F.cross_entropy(logits_q, y_qry[i])
                  losses_q[k + 1] += loss_q
  
                  with torch.no_grad():
                      pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)
                      correct = torch.eq(pred_q, y_qry[i]).sum().item()  # convert to numpy
                      corrects[k + 1] = corrects[k + 1] + correct
  
  
  
          # end of all tasks
          # sum over all losses on query set across all tasks
          loss_q = losses_q[-1] / task_num
  
          # optimize theta parameters
          self.meta_optim.zero_grad()#梯度初始化为0
          #net: 前向传播求预测值
          #criterion: 求loss
          loss_q.backward()#反向传播求梯度
          # print('meta update')
          # for p in self.net.parameters()[:5]:
          # 	print(torch.norm(p).item())
          self.meta_optim.step()#更新所有参数
  
  
          accs = np.array(corrects) / (querysz * task_num)
  
          return accs
  
  
      def finetunning(self, x_spt, y_spt, x_qry, y_qry):
          """
  
          :param x_spt:   [setsz, c_, h, w]
          :param y_spt:   [setsz]
          :param x_qry:   [querysz, c_, h, w]
          :param y_qry:   [querysz]
          :return:
          """
          assert len(x_spt.shape) == 4
  
          querysz = x_qry.size(0)
  
          corrects = [0 for _ in range(self.update_step_test + 1)]
  
          # in order to not ruin the state of running_mean/variance and bn_weight/bias
          # we finetunning on the copied model instead of self.net
          net = deepcopy(self.net)
  
          # 1. run the i-th task and compute loss for k=0
          print("+++shape in complexmeta+++")
          print(x_spt.shape)
          logits = net(x_spt)
          print("logits ",logits.shape)
          print("+++++++")
          loss = F.cross_entropy(logits, y_spt.long())
          grad = torch.autograd.grad(loss, net.parameters())
          fast_weights = list(map(lambda p: p[1] - self.update_lr * p[0], zip(grad, net.parameters())))
  
          # this is the loss and accuracy before first update
          with torch.no_grad():
              # [setsz, nway]
              logits_q = net(x_qry, net.parameters(), bn_training=True)
              # [setsz]
              pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)
              # scalar
              correct = torch.eq(pred_q, y_qry.long()).sum().item()
              corrects[0] = corrects[0] + correct
  
          # this is the loss and accuracy after the first update
          with torch.no_grad():
              # [setsz, nway]
              logits_q = net(x_qry, fast_weights, bn_training=True)
              # [setsz]
              pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)
              # scalar
              correct = torch.eq(pred_q, y_qry.long()).sum().item()
              corrects[1] = corrects[1] + correct
  
          for k in range(1, self.update_step_test):
              # 1. run the i-th task and compute loss for k=1~K-1
              logits = net(x_spt, fast_weights, bn_training=True)
              loss = F.cross_entropy(logits, y_spt.long())
              # 2. compute grad on theta_pi
              grad = torch.autograd.grad(loss, fast_weights)
              # 3. theta_pi = theta_pi - train_lr * grad
              fast_weights = list(map(lambda p: p[1] - self.update_lr * p[0], zip(grad, fast_weights)))
  
              logits_q = net(x_qry, fast_weights, bn_training=True)
              # loss_q will be overwritten and just keep the loss_q on last update step.
              loss_q = F.cross_entropy(logits_q, y_qry.long())
  
              with torch.no_grad():
                  pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)
                  correct = torch.eq(pred_q, y_qry.long()).sum().item()  # convert to numpy 取单元素张量的元素值
                  corrects[k + 1] = corrects[k + 1] + correct
  
  
          del net
  
          accs = np.array(corrects) / querysz
  
          return accs
  
      def confuse(self, x_spt, y_spt, x_qry, y_qry):
          """
  
          :param x_spt:   [setsz, c_, h, w]
          :param y_spt:   [setsz]
          :param x_qry:   [querysz, c_, h, w]
          :param y_qry:   [querysz]
          :return:
          """
          assert len(x_spt.shape) == 4
  
          querysz = x_qry.size(0)
  
          corrects = [0 for _ in range(self.update_step_test + 1)]
  
          c_matrix = [[0 for j in range(5)] for i in range(5)]
          #print(c_matrix)
          # in order to not ruin the state of running_mean/variance and bn_weight/bias
          # we finetunning on the copied model instead of self.net
          net = deepcopy(self.net)
  
          # 1. run the i-th task and compute loss for k=0
          logits = net(x_spt)
          loss = F.cross_entropy(logits, y_spt.long())
          grad = torch.autograd.grad(loss, net.parameters())
          fast_weights = list(map(lambda p: p[1] - self.update_lr * p[0], zip(grad, net.parameters())))
          #test_accs = []
          #index = 1;
          workbook = xlwt.Workbook()
          sheet = workbook.add_sheet("sheet 1")
          #col = 0;
  
          # this is the loss and accuracy before first update
          with torch.no_grad():
              # [setsz, nway]
              logits_q = net(x_qry, net.parameters(), bn_training=True)
              # [setsz]
              pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)
              # scalar
              correct = torch.eq(pred_q, y_qry.long()).sum().item()
              corrects[0] = corrects[0] + correct
  
          # this is the loss and accuracy after the first update
          with torch.no_grad():
              # [setsz, nway]
              logits_q = net(x_qry, fast_weights, bn_training=True)
              # [setsz]
              pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)
              # scalar
              correct = torch.eq(pred_q, y_qry.long()).sum().item()
              corrects[1] = corrects[1] + correct
  
          for k in range(1, self.update_step_test):
              # 1. run the i-th task and compute loss for k=1~K-1
              logits = net(x_spt, fast_weights, bn_training=True)
              loss = F.cross_entropy(logits, y_spt.long())
              # 2. compute grad on theta_pi
              grad = torch.autograd.grad(loss, fast_weights)
              # 3. theta_pi = theta_pi - train_lr * grad
              fast_weights = list(map(lambda p: p[1] - self.update_lr * p[0], zip(grad, fast_weights)))
  
              logits_q = net(x_qry, fast_weights, bn_training=True)
              # loss_q will be overwritten and just keep the loss_q on last update step.
              loss_q = F.cross_entropy(logits_q, y_qry.long())
  
              with torch.no_grad():
                  pred_q = F.softmax(logits_q, dim=1).argmax(dim=1)
                  if(k>=5):
                      for i in range(querysz):
                          c_matrix[y_qry[i].item()][pred_q[i].item()]=c_matrix[y_qry[i].item()][pred_q[i].item()]+1
  
                  correct = torch.eq(pred_q, y_qry.long()).sum().item()  # convert to numpy 取单元素张量的元素值
                  corrects[k + 1] = corrects[k + 1] + correct
  
          for i in range(5):
              for j in range(5):
                  #print(c_matrix[i][j]/querysz/2)
                  sheet.write(i, j, str(c_matrix[i][j]))
  
          workbook.save("RML_1shot_CAMEL_con.xls")
          del net
  
          accs = np.array(corrects) / querysz
  
          return accs,workbook
  
  
  
  
  def main():
      pass
  
  
  if __name__ == '__main__':
      main()
  