Tree Nested PyTorch Tensor Lib

Overview

DI-treetensor

PyPI PyPI - Python Version Loc Comments

Docs Deploy Code Test Badge Creation Package Release codecov

GitHub stars GitHub forks GitHub commit activity GitHub issues GitHub pulls Contributors GitHub license

treetensor is a generalized tree-based tensor structure mainly developed by OpenDILab Contributors.

Almost all the operation can be supported in form of trees in a convenient way to simplify the structure processing when the calculation is tree-based.

Installation

You can simply install it with pip command line from the official PyPI site.

pip install di-treetensor

For more information about installation, you can refer to Installation.

Documentation

The detailed documentation are hosted on https://opendilab.github.io/DI-treetensor.

Only english version is provided now, the chinese documentation is still under development.

Quick Start

You can easily create a tree value object based on FastTreeValue.

import builtins
import os
from functools import partial

import treetensor.torch as torch

print = partial(builtins.print, sep=os.linesep)

if __name__ == '__main__':
    # create a tree tensor
    t = torch.randn({'a': (2, 3), 'b': {'x': (3, 4)}})
    print(t)
    print(torch.randn(4, 5))  # create a normal tensor
    print()

    # structure of tree
    print('Structure of tree')
    print('t.a:', t.a)  # t.a is a native tensor
    print('t.b:', t.b)  # t.b is a tree tensor
    print('t.b.x', t.b.x)  # t.b.x is a native tensor
    print()

    # math calculations
    print('Math calculation')
    print('t ** 2:', t ** 2)
    print('torch.sin(t).cos()', torch.sin(t).cos())
    print()

    # backward calculation
    print('Backward calculation')
    t.requires_grad_(True)
    t.std().arctan().backward()
    print('grad of t:', t.grad)
    print()

    # native operation
    # all the ops can be used as the original usage of `torch`
    print('Native operation')
    print('torch.sin(t.a)', torch.sin(t.a))  # sin of native tensor

The result should be

<Tensor 0x7f0dae602760>
├── a --> tensor([[-1.2672, -1.5817, -0.3141],
│                 [ 1.8107, -0.1023,  0.0940]])
└── b --> <Tensor 0x7f0dae602820>
    └── x --> tensor([[ 1.2224, -0.3445, -0.9980, -0.4085],
                      [ 1.5956,  0.8825, -0.5702, -0.2247],
                      [ 0.9235,  0.4538,  0.8775, -0.2642]])

tensor([[-0.9559,  0.7684,  0.2682, -0.6419,  0.8637],
        [ 0.9526,  0.2927, -0.0591,  1.2804, -0.2455],
        [ 0.4699, -0.9998,  0.6324, -0.6885,  1.1488],
        [ 0.8920,  0.4401, -0.7785,  0.5931,  0.0435]])

Structure of tree
t.a:
tensor([[-1.2672, -1.5817, -0.3141],
        [ 1.8107, -0.1023,  0.0940]])
t.b:
<Tensor 0x7f0dae602820>
└── x --> tensor([[ 1.2224, -0.3445, -0.9980, -0.4085],
                  [ 1.5956,  0.8825, -0.5702, -0.2247],
                  [ 0.9235,  0.4538,  0.8775, -0.2642]])

t.b.x
tensor([[ 1.2224, -0.3445, -0.9980, -0.4085],
        [ 1.5956,  0.8825, -0.5702, -0.2247],
        [ 0.9235,  0.4538,  0.8775, -0.2642]])

Math calculation
t ** 2:
<Tensor 0x7f0dae602eb0>
├── a --> tensor([[1.6057, 2.5018, 0.0986],
│                 [3.2786, 0.0105, 0.0088]])
└── b --> <Tensor 0x7f0dae60c040>
    └── x --> tensor([[1.4943, 0.1187, 0.9960, 0.1669],
                      [2.5458, 0.7789, 0.3252, 0.0505],
                      [0.8528, 0.2059, 0.7699, 0.0698]])

torch.sin(t).cos()
<Tensor 0x7f0dae621910>
├── a --> tensor([[0.5782, 0.5404, 0.9527],
│                 [0.5642, 0.9948, 0.9956]])
└── b --> <Tensor 0x7f0dae6216a0>
    └── x --> tensor([[0.5898, 0.9435, 0.6672, 0.9221],
                      [0.5406, 0.7163, 0.8578, 0.9753],
                      [0.6983, 0.9054, 0.7185, 0.9661]])


Backward calculation
grad of t:
<Tensor 0x7f0dae60c400>
├── a --> tensor([[-0.0435, -0.0535, -0.0131],
│                 [ 0.0545, -0.0064, -0.0002]])
└── b --> <Tensor 0x7f0dae60cbe0>
    └── x --> tensor([[ 0.0357, -0.0141, -0.0349, -0.0162],
                      [ 0.0476,  0.0249, -0.0213, -0.0103],
                      [ 0.0262,  0.0113,  0.0248, -0.0116]])


Native operation
torch.sin(t.a)
tensor([[-0.9543, -0.9999, -0.3089],
        [ 0.9714, -0.1021,  0.0939]], grad_fn=<SinBackward>)

For more quick start explanation and further usage, take a look at:

Extension

If you need to translate treevalue object to runnable source code, you may use the potc-treevalue plugin with the installation command below

pip install DI-treetensor[potc]

In potc, you can translate the objects to runnable python source code, which can be loaded to objects afterwards by the python interpreter, like the following graph

potc_system

For more information, you can refer to

Contribution

We appreciate all contributions to improve DI-treetensor, both logic and system designs. Please refer to CONTRIBUTING.md for more guides.

And users can join our slack communication channel, or contact the core developer HansBug for more detailed discussion.

License

DI-treetensor released under the Apache 2.0 license.

You might also like...
 Pretty Tensor - Fluent Neural Networks in TensorFlow
Pretty Tensor - Fluent Neural Networks in TensorFlow

Pretty Tensor provides a high level builder API for TensorFlow. It provides thin wrappers on Tensors so that you can easily build multi-layer neural networks.

A torch.Tensor-like DataFrame library supporting multiple execution runtimes and Arrow as a common memory format

TorchArrow (Warning: Unstable Prototype) This is a prototype library currently under heavy development. It does not currently have stable releases, an

Gradient-free global optimization algorithm for multidimensional functions based on the low rank tensor train format

ttopt Description Gradient-free global optimization algorithm for multidimensional functions based on the low rank tensor train (TT) format and maximu

 (Py)TOD: Tensor-based Outlier Detection, A General GPU-Accelerated Framework
(Py)TOD: Tensor-based Outlier Detection, A General GPU-Accelerated Framework

(Py)TOD: Tensor-based Outlier Detection, A General GPU-Accelerated Framework Background: Outlier detection (OD) is a key data mining task for identify

Code to reproduce the results in the paper
Code to reproduce the results in the paper "Tensor Component Analysis for Interpreting the Latent Space of GANs".

Tensor Component Analysis for Interpreting the Latent Space of GANs [ paper | project page ] Code to reproduce the results in the paper "Tensor Compon

Self-Correcting Quantum Many-Body Control using Reinforcement Learning with Tensor Networks

Self-Correcting Quantum Many-Body Control using Reinforcement Learning with Tensor Networks This repository contains the code and data for the corresp

mbrl-lib is a toolbox for facilitating development of Model-Based Reinforcement Learning algorithms.
mbrl-lib is a toolbox for facilitating development of Model-Based Reinforcement Learning algorithms.

mbrl-lib is a toolbox for facilitating development of Model-Based Reinforcement Learning algorithms. It provides easily interchangeable modeling and planning components, and a set of utility functions that allow writing model-based RL algorithms with only a few lines of code.

OpenDILab RL Kubernetes Custom Resource and Operator Lib

DI Orchestrator DI Orchestrator is designed to manage DI (Decision Intelligence) jobs using Kubernetes Custom Resource and Operator. Prerequisites A w

Jittor Medical Segmentation Lib -- The assignment of Pattern Recognition course (2021 Spring) in Tsinghua University
Jittor Medical Segmentation Lib -- The assignment of Pattern Recognition course (2021 Spring) in Tsinghua University

THU模式识别2021春 -- Jittor 医学图像分割 模型列表 本仓库收录了课程作业中同学们采用jittor框架实现的如下模型: UNet SegNet DeepLab V2 DANet EANet HarDNet及其改动HarDNet_alter PSPNet OCNet OCRNet DL

Comments
  • PyTorch OP List(P0)

    PyTorch OP List(P0)

    reference: https://pytorch.org/docs/1.8.0/torch.html

    common

    • [x] numel
    • [x] cpu
    • [x] cuda
    • [x] to

    Creation Ops

    • [x] torch.zeros_like
    • [x] torch.randn_like
    • [x] torch.randint_like
    • [x] torch.ones_like
    • [x] torch.full_like
    • [x] torch.empty_like
    • [x] torch.zeros
    • [x] torch.randn
    • [x] torch.randint
    • [x] torch.ones
    • [x] torch.full
    • [x] torch.empty

    Indexing, Slicing, Joining, Mutating Ops

    • [x] cat
    • [x] chunk
    • [ ] gather
    • [x] index_select
    • [x] masked_select
    • [x] reshape
    • [ ] scatter
    • [x] split
    • [x] squeeze
    • [x] stack
    • [ ] tile
    • [ ] unbind
    • [x] unsqueeze
    • [x] where

    Math Ops

    Pointwise Ops
    • [x] add
    • [x] sub
    • [x] mul
    • [x] div
    • [x] pow
    • [x] neg
    • [x] abs
    • [x] sign
    • [x] floor
    • [x] ceil
    • [x] round
    • [x] sigmoid
    • [x] clamp
    • [x] exp
    • [x] exp2
    • [x] sqrt
    • [x] log
    • [x] log10
    • [x] log2
    Reduction Ops
    • [ ] argmax
    • [ ] argmin
    • [x] all
    • [x] any
    • [x] max
    • [x] min
    • [x] dist
    • [ ] logsumexp
    • [x] mean
    • [ ] median
    • [x] norm
    • [ ] prod
    • [x] std
    • [x] sum
    • [ ] unique
    Comparison Ops
    • [ ] argsort
    • [x] eq
    • [x] ge
    • [x] gt
    • [x] isfinite
    • [x] isinf
    • [x] isnan
    • [x] le
    • [x] lt
    • [x] ne
    • [ ] sort
    • [ ] topk
    Other Ops
    • [ ] cdist
    • [x] clone
    • [ ] flip

    BLAS and LAPACK Ops

    • [ ] addbmm
    • [ ] addmm
    • [ ] bmm
    • [x] dot
    • [x] matmul
    • [x] mm
    enhancement 
    opened by PaParaZz1 3
  • PyTorch OP Doc List

    PyTorch OP Doc List

    P0

    • [x] cpu
    • [x] cuda
    • [x] to
    • [x] torch.zeros_like
    • [x] torch.randn_like
    • [x] torch.ones_like
    • [x] torch.zeros
    • [x] torch.randn
    • [x] torch.randint
    • [x] torch.ones
    • [x] cat
    • [x] reshape
    • [x] split
    • [x] squeeze
    • [x] stack
    • [x] unsqueeze
    • [x] where
    • [x] abs
    • [x] add
    • [x] clamp
    • [x] div
    • [x] exp
    • [x] log
    • [x] sqrt
    • [x] sub
    • [x] sigmoid
    • [x] pow
    • [x] mul
    • [ ] argmax
    • [ ] argmin
    • [x] all
    • [x] any
    • [x] max
    • [x] min
    • [x] dist
    • [x] mean
    • [x] std
    • [x] sum
    • [x] eq
    • [x] ge
    • [x] gt
    • [x] le
    • [x] lt
    • [x] ne
    • [x] clone
    • [x] dot
    • [x] matmul
    • [x] mm

    P1

    • [x] numel
    • [x] torch.randint_like
    • [x] torch.full_like
    • [x] torch.empty_like
    • [x] torch.full
    • [x] torch.empty
    • [x] chunk
    • [ ] gather
    • [x] index_select
    • [x] masked_select
    • [ ] scatter
    • [ ] tile
    • [ ] unbind
    • [x] ceil
    • [x] exp2
    • [x] floor
    • [x] log10
    • [x] log2
    • [x] neg
    • [x] round
    • [x] sign
    • [ ] bmm

    P2

    • [ ] logsumexp
    • [ ] median
    • [x] norm
    • [ ] prod
    • [ ] unique
    • [ ] argsort
    • [x] isfinite
    • [x] isinf
    • [x] isnan
    • [ ] sort
    • [ ] topk
    • [ ] cdist
    • [ ] flip
    • [ ] addbmm
    • [ ] addmm
    opened by PaParaZz1 2
  • dev(hansbug): add stream support for paralleling the calculations in tree

    dev(hansbug): add stream support for paralleling the calculations in tree

    Here is an example:

    import time
    
    import numpy as np
    import torch
    
    import treetensor.torch as ttorch
    
    N, M, T = 200, 2, 50
    S1, S2, S3 = 512, 1024, 2048
    
    
    def test_min():
        a = ttorch.randn({f'a{i}': (S1, S2) for i in range(N // M)}, device='cuda')
        b = ttorch.randn({f'a{i}': (S2, S3) for i in range(N // M)}, device='cuda')
    
        result = []
        for i in range(T):
            _start_time = time.time()
    
            _ = ttorch.matmul(a, b)
            torch.cuda.synchronize()
    
            _end_time = time.time()
            result.append(_end_time - _start_time)
    
        print('time cost: mean({}) std({})'.format(np.mean(result), np.std(result)))
    
    
    def test_native():
        a = {f'a{i}': torch.randn(S1, S2, device='cuda') for i in range(N)}
        b = {f'a{i}': torch.randn(S2, S3, device='cuda') for i in range(N)}
    
        result = []
        for i in range(T):
            _start_time = time.time()
    
            for key in a.keys():
                _ = torch.matmul(a[key], b[key])
            torch.cuda.synchronize()
    
            _end_time = time.time()
            result.append(_end_time - _start_time)
    
        print('time cost: mean({}) std({})'.format(np.mean(result), np.std(result)))
    
    
    def test_linear():
        a = ttorch.randn({f'a{i}': (S1, S2) for i in range(N)}, device='cuda')
        b = ttorch.randn({f'a{i}': (S2, S3) for i in range(N)}, device='cuda')
    
        result = []
        for i in range(T):
            _start_time = time.time()
    
            _ = ttorch.matmul(a, b)
            torch.cuda.synchronize()
    
            _end_time = time.time()
            result.append(_end_time - _start_time)
    
        print('time cost: mean({}) std({})'.format(np.mean(result), np.std(result)))
    
    
    def test_stream():
        a = ttorch.randn({f'a{i}': (S1, S2) for i in range(N)}, device='cuda')
        b = ttorch.randn({f'a{i}': (S2, S3) for i in range(N)}, device='cuda')
    
        ttorch.stream(M)
        result = []
        for i in range(T):
            _start_time = time.time()
    
            _ = ttorch.matmul(a, b)
            torch.cuda.synchronize()
    
            _end_time = time.time()
            result.append(_end_time - _start_time)
    
        print('time cost: mean({}) std({})'.format(np.mean(result), np.std(result)))
    
    
    def warmup():
        # warm up
        a = torch.randn(1024, 1024).cuda()
        b = torch.randn(1024, 1024).cuda()
        for _ in range(20):
            c = torch.matmul(a, b)
    
    
    if __name__ == '__main__':
        warmup()
        test_min()
        test_native()
        test_linear()
        test_stream()
    
    

    不过讲真,这个stream实际效果挺脆弱的,非常看tensor尺寸,大了小了都不行,GPU性能不够也不行,一弄不好还容易负优化,总之挺难伺候的。这部分如果想实用化的话得再研究研究。

    enhancement 
    opened by HansBug 1
  • Failure when try to convert between numpy and torch on Windows Python3.10

    Failure when try to convert between numpy and torch on Windows Python3.10

    See here: https://github.com/opendilab/DI-treetensor/runs/7820313811?check_suite_focus=true

    The bug is like

        @method_treelize(return_type=_get_tensor_class)
        def tensor(self: numpy.ndarray, *args, **kwargs):
    >       tensor_: torch.Tensor = torch.from_numpy(self)
    E       RuntimeError: Numpy is not available
    

    The only way I found to 'solve' this is to downgrade python to version3.9 to lower. So these tests will be skipped temporarily.

    bug 
    opened by HansBug 0
Releases(v0.4.0)
  • v0.4.0(Aug 14, 2022)

    What's Changed

    • dev(hansbug): remove support for py3.6 by @HansBug in https://github.com/opendilab/DI-treetensor/pull/12
    • pytorch upgrade to 1.12 by @zjowowen in https://github.com/opendilab/DI-treetensor/pull/11
    • dev(hansbug): add test for torch1.12.0 and python3.10 by @HansBug in https://github.com/opendilab/DI-treetensor/pull/13
    • dev(hansbug): add stream support for paralleling the calculations in tree by @HansBug in https://github.com/opendilab/DI-treetensor/pull/10

    New Contributors

    • @zjowowen made their first contribution in https://github.com/opendilab/DI-treetensor/pull/11

    Full Changelog: https://github.com/opendilab/DI-treetensor/compare/v0.3.0...v0.4.0

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Jul 15, 2022)

    What's Changed

    • dev(hansbug): use newer version of treevalue 1.4.1 by @HansBug in https://github.com/opendilab/DI-treetensor/pull/9

    Full Changelog: https://github.com/opendilab/DI-treetensor/compare/v0.2.1...v0.3.0

    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(Mar 22, 2022)

    What's Changed

    • fix(hansbug): fix uncompitable problem with walk by @HansBug in https://github.com/opendilab/DI-treetensor/pull/5
    • dev(hansbug): add tensor method for treetensor.numpy.ndarray by @HansBug in https://github.com/opendilab/DI-treetensor/pull/6
    • fix(hansbug): add subside support to all the functions. by @HansBug in https://github.com/opendilab/DI-treetensor/pull/7
    • doc(hansbug): add documentation for np.stack, np.split and other 3 functions. by @HansBug in https://github.com/opendilab/DI-treetensor/pull/8
    • release(hansbug): use version 0.2.1 by @HansBug in https://github.com/opendilab/DI-treetensor/pull/4

    New Contributors

    • @HansBug made their first contribution in https://github.com/opendilab/DI-treetensor/pull/5

    Full Changelog: https://github.com/opendilab/DI-treetensor/compare/v0.2.0...v0.2.1

    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(Jan 4, 2022)

    • Use newer version of treevalue>=1.2.0
    • Add support of torch 1.10.0
    • Add support of potc

    Full Changelog: https://github.com/opendilab/DI-treetensor/compare/v0.1.0...v0.2.0

    Source code(tar.gz)
    Source code(zip)
  • v0.1.0(Dec 26, 2021)

  • v0.0.1(Sep 30, 2021)

Owner
OpenDILab
Open sourced Decision Intelligence (DI)
OpenDILab
Auxiliary Raw Net (ARawNet) is a ASVSpoof detection model taking both raw waveform and handcrafted features as inputs, to balance the trade-off between performance and model complexity.

Overview This repository is an implementation of the Auxiliary Raw Net (ARawNet), which is ASVSpoof detection system taking both raw waveform and hand

6 Jul 08, 2022
Implementation of the federated dual coordinate descent (FedDCD) method.

FedDCD.jl Implementation of the federated dual coordinate descent (FedDCD) method. Installation To install, just call Pkg.add("https://github.com/Zhen

Zhenan Fan 6 Sep 21, 2022
a Pytorch easy re-implement of "YOLOX: Exceeding YOLO Series in 2021"

A pytorch easy re-implement of "YOLOX: Exceeding YOLO Series in 2021" 1. Notes This is a pytorch easy re-implement of "YOLOX: Exceeding YOLO Series in

91 Dec 26, 2022
A Simple Long-Tailed Rocognition Baseline via Vision-Language Model

BALLAD This is the official code repository for A Simple Long-Tailed Rocognition Baseline via Vision-Language Model. Requirements Python3 Pytorch(1.7.

Teli Ma 4 Jan 20, 2022
Lenia - Mathematical Life Forms

For full version list, see Timeline in Lenia portal [2020-10-13] Update Python version with multi-kernel and multi-channel extensions (v3.4 LeniaNDK.p

Bert Chan 3.1k Dec 28, 2022
Pre-Trained Image Processing Transformer (IPT)

Pre-Trained Image Processing Transformer (IPT) By Hanting Chen, Yunhe Wang, Tianyu Guo, Chang Xu, Yiping Deng, Zhenhua Liu, Siwei Ma, Chunjing Xu, Cha

HUAWEI Noah's Ark Lab 332 Dec 18, 2022
Object detection (YOLO) with pytorch, OpenCV and python

Real Time Object/Face Detection Using YOLO-v3 This project implements a real time object and face detection using YOLO algorithm. You only look once,

1 Aug 04, 2022
FairEdit: Preserving Fairness in Graph Neural Networks through Greedy Graph Editing

FairEdit Relevent Publication FairEdit: Preserving Fairness in Graph Neural Networks through Greedy Graph Editing

5 Feb 04, 2022
Baseline model for "GraspNet-1Billion: A Large-Scale Benchmark for General Object Grasping" (CVPR 2020)

GraspNet Baseline Baseline model for "GraspNet-1Billion: A Large-Scale Benchmark for General Object Grasping" (CVPR 2020). [paper] [dataset] [API] [do

GraspNet 209 Dec 29, 2022
FPSAutomaticAiming——基于YOLOV5的FPS类游戏自动瞄准AI

FPSAutomaticAiming——基于YOLOV5的FPS类游戏自动瞄准AI 声明: 本项目仅限于学习交流,不可用于非法用途,包括但不限于:用于游戏外挂等,使用本项目产生的任何后果与本人无关! 简介 本项目基于yolov5,实现了一款FPS类游戏(CF、CSGO等)的自瞄AI,本项目旨在使用现

Fabian 246 Dec 28, 2022
The repository offers the official implementation of our paper in PyTorch.

Cloth Interactive Transformer (CIT) Cloth Interactive Transformer for Virtual Try-On Bin Ren1, Hao Tang1, Fanyang Meng2, Runwei Ding3, Ling Shao4, Phi

Bingoren 49 Dec 01, 2022
DeepProbLog is an extension of ProbLog that integrates Probabilistic Logic Programming with deep learning by introducing the neural predicate.

DeepProbLog DeepProbLog is an extension of ProbLog that integrates Probabilistic Logic Programming with deep learning by introducing the neural predic

KU Leuven Machine Learning Research Group 94 Dec 18, 2022
Code for MarioNette: Self-Supervised Sprite Learning, in NeurIPS 2021

MarioNette | Webpage | Paper | Video MarioNette: Self-Supervised Sprite Learning Dmitriy Smirnov, Michaël Gharbi, Matthew Fisher, Vitor Guizilini, Ale

Dima Smirnov 28 Nov 18, 2022
Hand Gesture Volume Control | Open CV | Computer Vision

Gesture Volume Control Hand Gesture Volume Control | Open CV | Computer Vision Use gesture control to change the volume of a computer. First we look i

Jhenil Parihar 3 Jun 15, 2022
A web-based application for quick, scalable, and automated hyperparameter tuning and stacked ensembling in Python.

Xcessiv Xcessiv is a tool to help you create the biggest, craziest, and most excessive stacked ensembles you can think of. Stacked ensembles are simpl

Reiichiro Nakano 1.3k Nov 17, 2022
A sample pytorch Implementation of ACL 2021 research paper "Learning Span-Level Interactions for Aspect Sentiment Triplet Extraction".

Span-ASTE-Pytorch This repository is a pytorch version that implements Ali's ACL 2021 research paper Learning Span-Level Interactions for Aspect Senti

来自丹麦的天籁 10 Dec 06, 2022
An imperfect information game is a type of game with asymmetric information

DecisionHoldem An imperfect information game is a type of game with asymmetric information. Compared with perfect information game, imperfect informat

Decision AI 25 Dec 23, 2022
A powerful framework for decentralized federated learning with user-defined communication topology

Scatterbrained Decentralized Federated Learning Scatterbrained makes it easy to build federated learning systems. In addition to traditional federated

Johns Hopkins Applied Physics Laboratory 7 Sep 26, 2022
Keep CALM and Improve Visual Feature Attribution

Keep CALM and Improve Visual Feature Attribution Jae Myung Kim1*, Junsuk Choe1*, Zeynep Akata2, Seong Joon Oh1† * Equal contribution † Corresponding a

NAVER AI 90 Dec 07, 2022
Implementation of ReSeg using PyTorch

Implementation of ReSeg using PyTorch ReSeg: A Recurrent Neural Network-based Model for Semantic Segmentation Pascal-Part Annotations Pascal VOC 2010

Onur Kaplan 46 Nov 23, 2022