Skip to main content

Runtime de inferencia puro NumPy para extraccion de pesos, capas y activaciones de LLMs y CNNs

Project description

micronnx

Capa de extracción de pesos, capas y activaciones para sistemas de fusión de modelos. Parte del ecosistema UFM (Unified Fusion Model).

micronnx no es un framework de entrenamiento ni un motor de inferencia general. Es una pieza de recolección: carga cualquier modelo desde cualquier formato, extrae sus pesos y activaciones capa a capa, y los expone en una estructura unificada lista para que UFM tome decisiones de fusión.

Sin PyTorch. Sin TensorFlow. Solo NumPy.


Instalación

pip install micronnx

¿Qué hace micronnx?

  • Carga de modelos: GGUF (Q2_K a Q6_K, Q8_0, Q8_1, BF16, F16, F32), SafeTensors, HDF5/Keras, NPY/NPZ — todos lazy, sin cargar nada hasta pedirlo
  • Extracción de pesos: todos los tensores normalizados a float32, etiquetados con rol y capa
  • Extracción de activaciones: capa a capa — embed, attn, ffn, residual, norm, pool
  • Exportación unificada: uno o varios modelos a un solo .npz con índice completo (schema y hp incluidos)
  • Carga desde .npz: NpzModelLoader detecta arquitectura automáticamente (31 familias) incluso en .npz de versiones antiguas sin schema_name
  • Ops vectorizadas: RMSNorm, Attention GQA, RoPE, SwiGLU, GeGLU, Conv2D, GroupNorm y más — sin loops Python

Formatos soportados

GGUF (.gguf) Q2_K, Q3_K, Q4_0, Q4_1, Q4_K, Q5_0, Q5_1, Q5_K, Q6_K Q8_0, Q8_1 (corregido en v2), BF16, F16, F32

SafeTensors (.safetensors) F32, F16, BF16 (corregido en v2), F64 convertido a F32 automaticamente I8, I16, I32, I64 sin conversion

HDF5 / Keras (.h5, .keras) float32, float64 convertido a float32, lazy loading

NumPy (.npy, .npz) float32, float16 convertido a float32, mmap lazy para .npz


Arquitecturas detectadas automaticamente

GGUF: gguf_llama, gguf_gemma, gguf_gemma2, gguf_phi3 gguf_falcon, gguf_falcon40b, gguf_gpt2, gguf_gpt_neox gguf_bloom, gguf_mpt, gguf_bert, gguf_mixtral gguf_qwen_moe, gguf_olmo2

HuggingFace SafeTensors: hf_llama, hf_gemma, hf_gemma2, hf_phi2, hf_phi3 hf_falcon, hf_falcon40b, hf_gpt2, hf_gpt_neox hf_bloom, hf_mpt, hf_bert, hf_mixtral, hf_qwen_moe hf_chatglm, hf_cohere, hf_olmo2, hf_internlm2 hf_baichuan, hf_stablelm, hf_minicpm, hf_xverse, hf_t5

Cubre: LLaMA 1/2/3/3.1/3.2, Mistral, Qwen 1/2/2.5/3, SmolLM, Gemma 1/2/3, Phi-2/3/3.5/4, Falcon 7B/40B, GPT-2, Starcoder2, GPT-NeoX, Pythia, BLOOM, MPT, BERT, RoBERTa, DeBERTa, Mixtral, Qwen2-MoE, DeepSeek V2/V3, ChatGLM4, Cohere Command-R, Aya, OLMo2, InternLM2, Baichuan2, StableLM, MiniCPM, XVERSE, T5, Flan-T5, MobileNetV1, MobileNetV2, MobileNetV3


Uso rapido

import micronnx as nx

loader     = nx.GGUFLoader("model.gguf")
schema, hp = nx.detect_schema_gguf("model.gguf")
runner     = nx.ModelRunner(loader, schema, hp)
logits     = runner.forward(input_ids)

Exportar modelos a .npz

import micronnx as nx

# Un solo modelo
nx.export_to_npz("model.gguf", "model.npz")

# Varios modelos, un .npz por cada uno
nx.export_to_npz(
    ["model.gguf", "model.safetensors", "mobilenet.h5"],
    "outputs/"
)

# Varios modelos en un solo .npz fusionado
nx.export_to_npz(
    ["model.gguf", "model.safetensors", "mobilenet.h5"],
    "outputs/merged.npz",
    merge=True
)

# Con string separado por comas
nx.export_to_npz("model.gguf, model.safetensors", "outputs/", merge=False)

Inspeccionar un .npz

import micronnx as nx

nx.inspect_npz("outputs/merged.npz")
# Merged  : 2 modelos
# Total   : 269,030,016 params | 513.14 MB float16
# [SmolLM2-135M]  272 tensores | 30 capas | 256.57 MB | schema: gguf_llama
# [model]         272 tensores | 30 capas | 256.57 MB | schema: hf_llama

# Leer el indice sin cargar ningun tensor
idx = nx.load_index("outputs/merged.npz")
print(idx["n_models"])
print(idx["total_params"])
print(idx["models"].keys())

# Schema e hiperparametros de cada modelo
for name, info in idx["models"].items():
    print(name, info["schema_name"], info["hp"])

Cargar desde .npz sin archivo original

import micronnx as nx

# Ver que modelos hay
models = nx.list_models("outputs/merged.npz")
for name, info in models.items():
    print(name, info["schema_name"], info["has_hp"], info["has_activations"])

# Cargar directamente — detecta arquitectura automaticamente
loader     = nx.NpzModelLoader("outputs/merged.npz", "SmolLM2-135M-Instruct-Q4_K_M")
schema, hp = nx.detect_schema_npz("outputs/merged.npz", "SmolLM2-135M-Instruct-Q4_K_M")
runner     = nx.ModelRunner(loader, schema, hp, max_seq=512)

# Si el .npz no tiene schema_name (version antigua),
# se detecta automaticamente desde los nombres de tensores sin el archivo original

Extraer activaciones — LLM

import numpy as np
import micronnx as nx

# Desde archivo original
loader     = nx.GGUFLoader("SmolLM2-135M-Instruct-Q4_K_M.gguf")
schema, hp = nx.detect_schema_gguf("SmolLM2-135M-Instruct-Q4_K_M.gguf")
runner     = nx.ModelRunner(loader, schema, hp, max_seq=512)

# O desde .npz sin archivo original
loader     = nx.NpzModelLoader("outputs/merged.npz", "SmolLM2-135M-Instruct-Q4_K_M")
schema, hp = nx.detect_schema_npz("outputs/merged.npz", "SmolLM2-135M-Instruct-Q4_K_M")
runner     = nx.ModelRunner(loader, schema, hp, max_seq=512)

# Todas las activaciones
ext = nx.ActivationExtractor(runner)
ext.run(np.array([[1, 2, 3, 4, 5]], dtype=np.int64))
print(ext.keys())
# embed, attn_norm_0, post_attn_0, residual_attn_0,
# ffn_norm_0, post_ffn_0, residual_ffn_0, ..., final_norm

# One-shot sin instanciar
logits, acts = nx.ActivationExtractor.extract(
    runner,
    np.array([[1, 2, 3]], dtype=np.int64)
)

# Solo algunos hooks
ext = nx.ActivationExtractor(runner, hooks=["post_attn", "residual_ffn"])
ext.run(np.array([[1, 2, 3, 4, 5]], dtype=np.int64))

# Reducir la dimension de secuencia
ext = nx.ActivationExtractor(runner, reduce="last")   # ultimo token
ext = nx.ActivationExtractor(runner, reduce="mean")   # media de tokens

# Solo capas pares
ext = nx.ActivationExtractor(runner, layer_fn=lambda i: i % 2 == 0)

# Solo capas especificas por indice
ext = nx.ActivationExtractor(runner, layer_fn=[0, 5, 11, 23])

# Acceso a activaciones
hidden = ext.get("final_norm")       # KeyError claro si no fue capturado
attn5  = ext.get("post_attn_5")
for key, tensor in ext.items():
    print(key, tensor.shape)

Extraer activaciones — BERT

import numpy as np
import micronnx as nx

loader     = nx.SafeTensorsLoader("bert-base-uncased/model.safetensors")
schema, hp = nx.detect_schema_safetensors("bert-base-uncased/model.safetensors")
runner     = nx.ModelRunner(loader, schema, hp)

ext    = nx.ActivationExtractor(runner, hooks=["post_attn", "final_norm"])
hidden = ext.run_bert(
    np.array([[101, 2054, 2003, 102]], dtype=np.int64),
    attention_mask=np.ones((1, 4), dtype=np.int64)
)

# One-shot para BERT
hidden, acts = nx.ActivationExtractor.extract_bert(runner, input_ids)
cls_vector   = acts["final_norm"][0, 0]   # token [CLS]

Extraer activaciones — CNN (MobileNet)

import numpy as np
import micronnx as nx

raw    = nx.H5Loader("mobilenet_1_0_224_tf.h5")
mapped = nx.map_tensors(dict.fromkeys(raw.tensor_names), fmt="h5")
loader = nx.CanonicalLoader(raw, mapped)
runner = nx.CNNRunner(loader, n_blocks=13)

# Imagen 224x224x3 normalizada en [-1, 1]
image = np.random.uniform(-1, 1, (224, 224, 3)).astype(np.float32)

# Forward directo
probs = runner.forward(image)
print(f"clase: {probs.argmax()}, confianza: {probs.max():.3f}")

# Con extraccion de activaciones
ext   = nx.CNNActivationExtractor(runner, reduce="spatial")
probs = ext.run(image)
print(ext.keys())
# stem, block_0_dw, block_0_pw, ..., block_12_pw, pooled

# One-shot
probs, acts = nx.CNNActivationExtractor.extract(runner, image)
feat = acts["block_5_pw"]   # (C,) con reduce="spatial"

# MobileNetV2 / MobileNetV3
runner = nx.InvertedResidualRunner(loader, n_blocks=17)
probs  = runner.forward(image)

Guardar y leer activaciones en el .npz

import numpy as np
import micronnx as nx

merged = nx.export_to_npz(
    ["SmolLM2-135M-Instruct-Q4_K_M.gguf", "model.safetensors"],
    "outputs/merged.npz",
    merge=True
)

loader     = nx.NpzModelLoader(merged, "SmolLM2-135M-Instruct-Q4_K_M")
schema, hp = nx.detect_schema_npz(merged, "SmolLM2-135M-Instruct-Q4_K_M")
runner     = nx.ModelRunner(loader, schema, hp)

ext = nx.ActivationExtractor(runner, reduce="last")
ext.run(np.array([[1, 2, 3]], dtype=np.int64))

nx.save_activations(merged, ext.activations, model_key="SmolLM2-135M-Instruct-Q4_K_M")

# Leer despues sin recargar el modelo
acts = nx.load_activations(merged, model_key="SmolLM2-135M-Instruct-Q4_K_M")
print(acts["final_norm"].shape)

TensorRegistry — hasta 3 modelos lazy simultaneos

import micronnx as nx

# Registrar modelos — solo indexa nombres, no carga datos
nx.registry.register("llama3",   nx.GGUFLoader("llama3.gguf"),         fmt="gguf")
nx.registry.register("mistral",  nx.SafeTensorsLoader("mistral.st"),   fmt="safetensors")
nx.registry.register("mobilenet",nx.H5Loader("mobilenet.h5"),          fmt="h5")

# Consultar sin cargar nada — O(1)
nx.registry.has("llama3", "layers.5.attn.q.weight")   # True / False
nx.registry.list("llama3")                             # lista de canonicos

# Cargar un tensor — solo aqui se lee del disco
t = nx.registry.get("llama3", "layers.5.attn.q.weight")

# Al registrar un 4to modelo, el mas antiguo se expulsa automaticamente (LRU)
nx.registry.release("mistral")   # liberar manualmente
nx.registry.stats()              # {"models": [...], "slots_used": 2, ...}

# O instanciar un registry propio
reg = nx.TensorRegistry()
reg.register("modelo_a", loader_a)
reg.register("modelo_b", loader_b)

Loaders directos

import micronnx as nx

# GGUF — lazy, mmap, dequantiza bajo demanda
loader = nx.GGUFLoader("model.gguf")
print(loader.tensor_names[:5])
w = loader.load("token_embd.weight")
loader.close()

# SafeTensors — lazy, mmap, BF16/F64 correctos
loader = nx.SafeTensorsLoader("model.safetensors")
print(loader.shape("model.embed_tokens.weight"))    # sin cargar
print(loader.dtype("model.embed_tokens.weight"))    # sin cargar
w = loader.load("model.embed_tokens.weight")

# Cargar solo tensores de atencion (ahorra 30-70% de RAM)
weights = loader.load_all(filter_fn=lambda n: "attn" in n)

# Exportar sin pico de RAM — un tensor a la vez
for name, arr in loader.stream_load(keep_float16=True):
    pass   # procesar arr y liberar

loader.close()

# HDF5 / Keras — lazy, sin cargar hasta .load()
loader = nx.H5Loader("mobilenet.h5")
w = loader.load("conv1/kernel:0")
attrs = loader.attributes("conv1")   # metadata de la capa Keras
loader.close()

# NPY / NPZ — mmap lazy para .npz
loader = nx.NpyLoader("weights.npz")
w = loader.load("layer_0")

# Directorio de .npy (estilo JAX/Flax)
loader = nx.NpyLoader("checkpoints/")
w = loader.load("encoder/layers/0/attn/q")

# Desde .npz merged sin archivo original
loader = nx.NpzModelLoader("outputs/merged.npz", "model")
w = loader.load("model.embed_tokens.weight")
loader.close()

Detectar schema y arquitectura

import micronnx as nx

# Desde archivo original
schema, hp = nx.detect_schema_gguf("model.gguf")
print(schema)
# gguf_llama / gguf_gemma2 / gguf_phi3 / gguf_mixtral / gguf_qwen_moe ...
print(hp)
# {"n_layers": 30, "n_heads": 9, "n_kv_heads": 3, "n_embd": 576, "vocab_size": 49152}

schema, hp = nx.detect_schema_safetensors("model.safetensors")
schema, hp = nx.detect_schema_hf("model.safetensors")   # alias

# Desde .npz sin archivo original
schema, hp = nx.detect_schema_npz("outputs/merged.npz", "SmolLM2-135M-Instruct-Q4_K_M")

# Ver todos los schemas disponibles
print(list(nx.SCHEMAS.keys()))
# gguf_llama, gguf_gemma, gguf_gemma2, gguf_phi3, gguf_falcon,
# gguf_mixtral, gguf_qwen_moe, hf_llama, hf_gemma2, hf_bert ...

Mapeo canonico de tensores

import micronnx as nx

# Convertir nombres originales a nombres canonicos
loader = nx.GGUFLoader("model.gguf")
mapped = nx.map_tensors(dict.fromkeys(loader.tensor_names), fmt="gguf")

# Ver tensores sin mapear
unmapped = nx.find_unmapped(dict.fromkeys(loader.tensor_names), fmt="gguf", mapped=mapped)

# Resolver embeddings atados (embed <-> head)
mapped = nx.resolve_tied_embeddings(mapped)

# Detectar formato automaticamente
fmt = nx.detect_format(dict.fromkeys(loader.tensor_names))

# CanonicalLoader: traduce nombres canonicos a originales automaticamente
canonical = nx.CanonicalLoader(loader, mapped)
w = canonical.load("layers.0.attn.q.weight")   # nombre canonico
print("layers.0.attn.q.weight" in canonical)   # True

Ops NumPy directas

import numpy as np
import micronnx as nx

x = np.random.randn(1, 16, 576).astype(np.float32)
w = np.ones(576, dtype=np.float32)

# Normalizacion
x = nx.rmsnorm(x, w)
x = nx.layernorm(x, w, w)
x = nx.batchnorm(x, gamma, beta, mean, var)
x = nx.group_norm(x, num_groups=8, weight=w)
x = nx.instance_norm(x)

# Activaciones
x = nx.gelu(x)
x = nx.quick_gelu(x)       # CLIP, ViT
x = nx.silu(x)             # LLaMA, Mistral
x = nx.mish(x)             # YOLOv4
x = nx.hardswish(x)        # MobileNetV3
x = nx.leaky_relu(x, 0.01) # GANs, YOLO
x = nx.prelu(x, weight)    # ResNet
x = nx.elu(x)

# FFN
out = nx.swiglu(x, gate_w, up_w, down_w)          # LLaMA, Mistral, Qwen
out = nx.swiglu_fused(x, gate_up_w, down_w)       # Phi-3, InternLM2
out = nx.geglu(x, gate_w, up_w, down_w)           # Gemma
out = nx.geglu_fused(x, gate_up_w, down_w)        # T5v1.1, Flan-T5
out = nx.ffn_gelu(x, up_w, down_w)                # GPT-2, BLOOM
out = nx.ffn_relu(x, up_w, down_w)                # T5 original

# Atencion GQA (cubre MHA y MQA como casos especiales)
q   = np.random.randn(1, 4, 9, 64).astype(np.float32)
k   = np.random.randn(1, 4, 3, 64).astype(np.float32)
v   = np.random.randn(1, 4, 3, 64).astype(np.float32)
out = nx.attention(q, k, v, n_heads=9, n_kv_heads=3)

# SDPA directa sin reshape de heads (ViT, CLIP)
out = nx.scaled_dot_product(q, k, v, mask=None)

# RoPE — convencion LLaMA correcta
x   = np.random.randn(1, 4, 8, 64).astype(np.float32)
out = nx.rope(x, pos=0)
out = nx.rope(x, positions=np.array([0, 1, 2, 3]))

# CNN
img  = np.random.randn(112, 112, 32).astype(np.float32)
filt = np.random.randn(64, 32, 3, 3).astype(np.float32)   # formato PyTorch (C_out,C_in,kH,kW)
out  = nx.conv2d(img, filt, stride=1, padding=1)
out  = nx.depthwise_conv2d(img, dw_weight, stride=2)
out  = nx.global_avg_pool(out)
out  = nx.avg_pool2d(out, kernel_size=2)
out  = nx.max_pool2d(out, size=2, stride=2)
out  = nx.adaptive_avg_pool(out, output_size=(7, 7))
out  = nx.upsample_nearest(out, scale_factor=2)

API completa

Loaders nx.GGUFLoader(path) lazy, mmap, dequant bajo demanda nx.SafeTensorsLoader(path) lazy, mmap, BF16/F64 correctos nx.H5Loader(path) lazy, float64 convertido nx.NpyLoader(path) lazy mmap para .npz, dir para .npy nx.NpzModelLoader(npz_path, model_key) desde .npz sin archivo original

Deteccion de schema nx.detect_schema_gguf(path) (schema_name, hp) nx.detect_schema_safetensors(path) (schema_name, hp) nx.detect_schema_hf(path) (schema_name, hp) nx.detect_schema_npz(npz_path, key) (schema_name, hp) desde .npz nx.detect_schema_from_tensors(names, ...) desde lista de nombres de tensores nx.list_models(npz_path) metadata de todos los modelos nx.SCHEMAS dict con todos los schemas

TensorRegistry nx.registry instancia global (hasta 3 modelos LRU) nx.TensorRegistry() instancia propia reg.register(name, loader, fmt) indexa sin cargar datos reg.get(name, canonical) carga aqui — unico punto de I/O reg.has(name, canonical) O(1) sin cargar nada reg.list(name) lista de canonicos disponibles reg.release(name) liberar slot manualmente reg.stats() info de slots usados

Runtime LLM nx.ModelRunner(loader, schema_name, hp, max_seq=2048) nx.ActivationExtractor(runner, hooks=None, reduce=None, layer_fn=None, keep_copy=True) hooks: ["embed","post_attn","residual_ffn","final_norm",...] reduce: None | "last" | "mean" layer_fn: callable(i)->bool | list[int] nx.ActivationExtractor.extract(runner, input_ids, hooks, reduce) nx.ActivationExtractor.extract_bert(runner, input_ids, ...) nx.RoPECache / nx.KVCache / nx.WeightCache

Runtime CNN nx.CNNRunner(loader, n_blocks=13) MobileNetV1 nx.InvertedResidualRunner(loader, n_blocks=17) MobileNetV2 / V3 nx.CNNActivationExtractor(runner, reduce="spatial") nx.CNNActivationExtractor.extract(runner, image) nx.CanonicalLoader(raw_loader, mapped)

Canonico nx.map_tensors(tensors, fmt, strict=False) nx.find_unmapped(tensors, fmt, mapped) nx.resolve_tied_embeddings(mapped) nx.detect_format(tensors) nx.CanonicalTensor

Exportador nx.export_to_npz(src, dst, fmt=None, merge=False, verbose=True) nx.load_index(path) nx.inspect_npz(path, n=20) nx.save_activations(path, acts, model_key=None) nx.load_activations(path, model_key=None)

Ops — normalizacion nx.rmsnorm(x, weight, eps=1e-6) nx.layernorm(x, weight, bias=None, eps=1e-5) nx.batchnorm(x, gamma, beta, mean, var, eps=1e-3) nx.group_norm(x, num_groups, weight=None, bias=None) nx.instance_norm(x, weight=None, bias=None)

Ops — activaciones nx.softmax(x, axis=-1) nx.sigmoid(x) nx.relu(x) nx.leaky_relu(x, negative_slope=0.01) nx.elu(x, alpha=1.0) nx.prelu(x, weight) nx.gelu(x) nx.quick_gelu(x) nx.silu(x) nx.mish(x) nx.hardswish(x)

Ops — lineales nx.linear(x, weight, bias=None) nx.embedding(idx, weight)

Ops — FFN nx.swiglu(x, gate_w, up_w, down_w) nx.swiglu_fused(x, gate_up_w, down_w) nx.geglu(x, gate_w, up_w, down_w) nx.geglu_fused(x, gate_up_w, down_w) nx.ffn_gelu(x, up_w, down_w) nx.ffn_relu(x, up_w, down_w)

Ops — atencion nx.attention(q, k, v, n_heads, n_kv_heads, mask=None) nx.scaled_dot_product(q, k, v, mask=None) nx.rope(x, pos=None, base=10000.0, positions=None)

Ops — CNN nx.conv2d(x, weight, bias=None, stride=1, padding=0) nx.depthwise_conv2d(x, weight, stride=1, padding=1) nx.global_avg_pool(x) nx.avg_pool2d(x, kernel_size=2, stride=None, padding=0) nx.max_pool2d(x, size=2, stride=2) nx.adaptive_avg_pool(x, output_size) nx.upsample_nearest(x, scale_factor=2)


Correcciones v2

BF16 en SafeTensors — resultado silenciosamente incorrecto en v1. v1 convertia el valor numerico uint16 a uint32 antes de shiftear, lo cual es matematicamente incorrecto. v2 copia los bytes directamente a los bytes altos del float32, que es la definicion de BF16. Afectaba a: Mistral, LLaMA-3, Gemma, Phi y cualquier modelo HF en BF16.

Q8_1 en GGUF — scale ignorado en v1. v1 trataba los bytes int8 como float32 directos sin aplicar el scale d. v2 extrae d correctamente y devuelve d * qs.

RoPE — convencion incorrecta en v1. La segunda mitad usaba x1sin + x2cos en lugar de la convencion LLaMA canonica [-x2, x1]. Corregido en v2.

SwiGLU — usaba sigmoid(gate) en lugar de silu(gate). Silenciosamente incorrecto. silu(x) = x * sigmoid(x), no sigmoid(x).

conv2d / depthwise_conv2d — formato de pesos Keras vs PyTorch. v1 asumia (kH, kW, C_in, C_out). v2 usa formato PyTorch (C_out, C_in, kH, kW).

H5Loader — cargaba todos los arrays en el constructor (eager). Para un MobileNet eran ~100MB en RAM antes de pedir nada. v2 es lazy.

NpyLoader — cargaba el .npz completo en RAM en el constructor. v2 usa mmap_mode="r" — los arrays se leen del disco solo cuando se piden.

Exporter: se corrige defaul del archivo shora detcta mejor todo

Dependencias

numpy >= 1.24 pyfive (para archivos .h5 / .keras)


Parte del ecosistema UFM

micronnx es la capa de recoleccion de UFM (Unified Fusion Model). Su unica responsabilidad es exponer pesos y activaciones en una estructura uniforme. La logica de fusion, compatibilidad y ajuste fino vive en UFM, no aqui.


Licencia

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

micronnx-0.1.2.3.tar.gz (86.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

micronnx-0.1.2.3-py3-none-any.whl (87.9 kB view details)

Uploaded Python 3

File details

Details for the file micronnx-0.1.2.3.tar.gz.

File metadata

  • Download URL: micronnx-0.1.2.3.tar.gz
  • Upload date:
  • Size: 86.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.33.1

File hashes

Hashes for micronnx-0.1.2.3.tar.gz
Algorithm Hash digest
SHA256 e2e62c1f5ebb2ebb70a0f3d0dc64654bdab64617f8857b8ada95e6859decbcfa
MD5 d6c47196f588928e74182504f5d71b9c
BLAKE2b-256 518b44f679b0f128736a73ca05d7f04907fe144ac3fbb678a24d132b03c738e7

See more details on using hashes here.

File details

Details for the file micronnx-0.1.2.3-py3-none-any.whl.

File metadata

  • Download URL: micronnx-0.1.2.3-py3-none-any.whl
  • Upload date:
  • Size: 87.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.33.1

File hashes

Hashes for micronnx-0.1.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 042dafc0b483e1140391d7be161af3aa7bb95aea65ec7a6e88e2eaa5ee2b74b5
MD5 b57184fbe1b5e1ed6d82bdaa0e546fff
BLAKE2b-256 b34d98f73128b90b60e5253facf9f0046431fce3996e86b0ff8c6bc2ba919406

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page