PSP - 蛋白质复合物结构预测 模版配对(Template Pair) 逻辑的特征分析

news2024/11/28 20:54:07

欢迎关注我的CSDN:https://spike.blog.csdn.net/
本文地址:https://spike.blog.csdn.net/article/details/134328447

在 蛋白质复合物结构预测 的过程中,模版 (Template) 起到重要作用,提供预测结果的关于三维结构的先验信息,在多链的情况,需要进行模版配对,即 Template Pair,核心函数是 template_pair_embedder,结合 AlphaFold2 的论文,分析具体输入与输出的特征。

核心逻辑 template_pair_embedder(),输入和输出特征维度:

[CL] TemplateEmbedderMultimer - template_dgram: torch.Size([1, 1102, 1102, 39])
[CL] TemplateEmbedderMultimer - z: torch.Size([1102, 1102, 128])
[CL] TemplateEmbedderMultimer - pseudo_beta_mask: torch.Size([1, 1102])
[CL] TemplateEmbedderMultimer - backbone_mask: torch.Size([1, 1102])
[CL] TemplateEmbedderMultimer - multichain_mask_2d: torch.Size([1102, 1102])
[CL] TemplateEmbedderMultimer - unit_vector: torch.Size([1, 1102, 1102])
[CL] TemplateEmbedderMultimer - pair_act: torch.Size([1, 1102, 1102, 64])

函数:

# openfold/model/embedders.py
pair_act = self.template_pair_embedder(
    template_dgram,
    aatype_one_hot,
    z,
    pseudo_beta_mask,
    backbone_mask,
    multichain_mask_2d,
    unit_vector,
)

t = torch.sum(t, dim=-4) / n_templ
t = torch.nn.functional.relu(t)
t = self.linear_t(t)  # 从 c_t 维度 转换 成 c_z 维度,更新 z
template_embeds["template_pair_embedding"] = t

# openfold/model/model.py
template_embeds = self.template_embedder(
    template_feats,
    z,
    pair_mask.to(dtype=z.dtype),
    no_batch_dims,
    chunk_size=self.globals.chunk_size,
    multichain_mask_2d=multichain_mask_2d,
    use_fa=self.globals.use_fa,
)
z = z + template_embeds["template_pair_embedding"]  # line 13 in Alg.2

逻辑图:

Template Pairing


1. template_dgram 特征

template_dgram特征计算,不同点距离其他点的远近划分,共划分 39 个bin,Template 的 no_bin 是 1.25 计算 1 个值,即 (50.75 - 3.25) / 38 = 1.25,即:

template_dgram = dgram_from_positions(
    template_positions,
    inf=self.config.inf,
    **self.config.distogram,
)

def dgram_from_positions(
    pos: torch.Tensor,
    min_bin: float = 3.25,
    max_bin: float = 50.75,
    no_bins: float = 39,
    inf: float = 1e8,
):
    dgram = torch.sum(
        (pos[..., None, :] - pos[..., None, :, :]) ** 2, dim=-1, keepdim=True
    )
    lower = torch.linspace(min_bin, max_bin, no_bins, device=pos.device) ** 2
    upper = torch.cat([lower[1:], lower.new_tensor([inf])], dim=-1)
    dgram = ((dgram > lower) * (dgram < upper)).type(dgram.dtype)

    return dgram

Template 的 no_bin 是 1.25 计算 1 个值,即 (50.75 - 3.25) / 38 = 1.25,长度是 39。

其中,template_positions 特征,如下:

  • 输入 template_pseudo_betatemplate_pseudo_beta_mask
  • 输出坐标 template_positions,即 template_pseudo_beta 的值
template_positions, pseudo_beta_mask = (
    single_template_feats["template_pseudo_beta"],
    single_template_feats["template_pseudo_beta_mask"],
)

其中pseudo_beta 特征的处理,来源于 openfold/data/data_transforms_multimer.py 即:

  • 输入特征:template_aatypetemplate_all_atom_positionstemplate_all_atom_mask
  • 原理是:选择 CA 或 CB 原子的坐标与 Mask

源码即调用关系如下:

# 输入特征,模型预测结构
# run_pretrained_openfold.py
processed_feature_dict, _ = feature_processor.process_features(
    feature_dict, is_multimer, mode="predict"
)
output_dict = predict_structure_single_dev(
    args,
    model_name,
    current_model,
    fasta_path,
    processed_feature_dict,
    config,
)
  
# openfold/data/feature_pipeline.py
processed_feature, label = np_example_to_features_multimer(
    np_example=raw_features,
    config=self.config,
    mode=mode,
)

# openfold/data/feature_pipeline.py
features, label = input_pipeline_multimer.process_tensors_from_config(
    tensor_dict,
    cfg.common,
    cfg[mode],
    cfg.data_module,
)

# openfold/data/input_pipeline_multimer.py
nonensembled = nonensembled_transform_fns(
    common_cfg,
    mode_cfg,
)
tensors = compose(nonensembled)(tensors)

# openfold/data/input_pipeline_multimer.py
operators.extend(
    [
        data_transforms_multimer.make_atom14_positions,
        data_transforms_multimer.atom37_to_frames,
        data_transforms_multimer.atom37_to_torsion_angles(""),
        data_transforms_multimer.make_pseudo_beta(""),
        data_transforms_multimer.get_backbone_frames,
        data_transforms_multimer.get_chi_angles,
    ]
)

# openfold/data/data_transforms_multimer.py
def make_pseudo_beta(protein, prefix=""):
    """Create pseudo-beta (alpha for glycine) position and mask."""
    assert prefix in ["", "template_"]
    (
        protein[prefix + "pseudo_beta"],
        protein[prefix + "pseudo_beta_mask"],
    ) = pseudo_beta_fn(
        protein["template_aatype" if prefix else "aatype"],
        protein[prefix + "all_atom_positions"],
        protein["template_all_atom_mask" if prefix else "all_atom_mask"],
    )
    return protein
  
# openfold/data/data_transforms_multimer.py
def pseudo_beta_fn(aatype, all_atom_positions, all_atom_mask):
    """Create pseudo beta features."""
    if aatype.shape[0] > 0:
        is_gly = torch.eq(aatype, rc.restype_order["G"])
        ca_idx = rc.atom_order["CA"]
        cb_idx = rc.atom_order["CB"]
        pseudo_beta = torch.where(
            torch.tile(is_gly[..., None], [1] * len(is_gly.shape) + [3]),
            all_atom_positions[..., ca_idx, :],
            all_atom_positions[..., cb_idx, :],
        )
    else:
        pseudo_beta = all_atom_positions.new_zeros(*aatype.shape, 3)
    if all_atom_mask is not None:
        if aatype.shape[0] > 0:
            pseudo_beta_mask = torch.where(
                is_gly, all_atom_mask[..., ca_idx], all_atom_mask[..., cb_idx]
            )
        else:
            pseudo_beta_mask = torch.zeros_like(aatype).float()
        return pseudo_beta, pseudo_beta_mask
    else:
        return pseudo_beta

template_pseudo_beta_mask: Mask indicating if the beta carbon (alpha carbon for glycine) atom has coordinates for the template at this residue.

template_dgram 特征 [1, 1102, 1102, 39],即:

template_dgram


2. z 特征

z 特征作为输入,直接传入,来源于 protein["target_feat"],来源于protein[]"between_segment_residues"], 即:

  • 日志target_feat: torch.Size([1102, 21]),不包括 “-”,只包括21=20+1个氨基酸,包括X
  • [1102, 21] 经过线性层,转换成 c_z 维度,即128维。
  • 再通过 outer sum 操作 转换成 LxLxC 维度,其实 z 就是 Pair Representation,即 [1102, 1102, 128] 维度。
# openfold/model/embedders.py
def forward(
    self,
    batch,
    z,
    padding_mask_2d,
    templ_dim,
    chunk_size,
    multichain_mask_2d,
    use_fa=False,
):
  
# openfold/model/model.py
template_embeds = self.template_embedder(
    template_feats,
    z,
    pair_mask.to(dtype=z.dtype),
    no_batch_dims,
    chunk_size=self.globals.chunk_size,
    multichain_mask_2d=multichain_mask_2d,
    use_fa=self.globals.use_fa,
)

# openfold/model/model.py
m, z = self.input_embedder(feats)

# openfold/model/embedders.py#InputEmbedderMultimer
def forward(self, batch) -> Tuple[torch.Tensor, torch.Tensor]:
    """
    # ...
    Returns:
        msa_emb:
            [*, N_clust, N_res, C_m] MSA embedding
        pair_emb:
            [*, N_res, N_res, C_z] pair embedding

    """
    tf = batch["target_feat"]
    msa = batch["msa_feat"]

    # [*, N_res, c_z]
    tf_emb_i = self.linear_tf_z_i(tf)
    tf_emb_j = self.linear_tf_z_j(tf)

    # [*, N_res, N_res, c_z]
    pair_emb = tf_emb_i[..., None, :] + tf_emb_j[..., None, :, :]
    pair_emb = pair_emb + self.relpos(batch)  # 计算相对位置

    # [*, N_clust, N_res, c_m]
    n_clust = msa.shape[-3]
    tf_m = (
        self.linear_tf_m(tf)
        .unsqueeze(-3)
        .expand(((-1,) * len(tf.shape[:-2]) + (n_clust, -1, -1)))
    )
    msa_emb = self.linear_msa_m(msa) + tf_m

    return msa_emb, pair_emb
  
# openfold/data/data_transforms_multimer.py
def create_target_feat(batch):
    """Create the target features"""
    batch["target_feat"] = torch.nn.functional.one_hot(batch["aatype"], 21).to(
        torch.float32
    )
    return batch

# openfold/data/input_pipeline_multimer.py
operators.extend(
    [
        data_transforms_multimer.cast_to_64bit_ints,
        # todo: randomly_replace_msa_with_unknown may need to be confirmed and tried in training.
        # data_transforms_multimer.randomly_replace_msa_with_unknown(0.0),
        data_transforms_multimer.make_msa_profile,
        data_transforms_multimer.create_target_feat,
        data_transforms_multimer.make_atom14_masks,
    ]
)

InputEmbedderMultimer 的框架图:
InputEmbedderMultimer

其中的 21 个氨基酸,即:

ID_TO_HHBLITS_AA = {
    0: "A",
    1: "C",  # Also U.
    2: "D",  # Also B.
    3: "E",  # Also Z.
    4: "F",
    5: "G",
    6: "H",
    7: "I",
    8: "K",
    9: "L",
    10: "M",
    11: "N",
    12: "P",
    13: "Q",
    14: "R",
    15: "S",
    16: "T",
    17: "V",
    18: "W",
    19: "Y",
    20: "X",  # Includes J and O.
    21: "-",
}

z 特征 (mean 和 max),[1102, 1102, 128],即:

z


3. pseudo_beta_mask 与 backbone_mask 特征

pseudo_beta_mask 特征 参考 template_dgramtemplate_pseudo_beta 部分的源码:

  • 关注 CA 与 CB 的 Mask 信息
# openfold/data/data_transforms_multimer.py
def make_pseudo_beta(protein, prefix=""):
    """Create pseudo-beta (alpha for glycine) position and mask."""
    assert prefix in ["", "template_"]
    (
        protein[prefix + "pseudo_beta"],
        protein[prefix + "pseudo_beta_mask"],
    ) = pseudo_beta_fn(
        protein["template_aatype" if prefix else "aatype"],
        protein[prefix + "all_atom_positions"],
        protein["template_all_atom_mask" if prefix else "all_atom_mask"],
    )
    return protein
  
# openfold/data/data_transforms_multimer.py
def pseudo_beta_fn(aatype, all_atom_positions, all_atom_mask):
    """Create pseudo beta features."""
    if aatype.shape[0] > 0:
        is_gly = torch.eq(aatype, rc.restype_order["G"])
        ca_idx = rc.atom_order["CA"]
        cb_idx = rc.atom_order["CB"]
        pseudo_beta = torch.where(
            torch.tile(is_gly[..., None], [1] * len(is_gly.shape) + [3]),
            all_atom_positions[..., ca_idx, :],
            all_atom_positions[..., cb_idx, :],
        )
    else:
        pseudo_beta = all_atom_positions.new_zeros(*aatype.shape, 3)
    if all_atom_mask is not None:
        if aatype.shape[0] > 0:
            pseudo_beta_mask = torch.where(
                is_gly, all_atom_mask[..., ca_idx], all_atom_mask[..., cb_idx]
            )
        else:
            pseudo_beta_mask = torch.zeros_like(aatype).float()
        return pseudo_beta, pseudo_beta_mask
    else:
        return pseudo_beta

其中,openfold/data/msa_pairing.py#merge_chain_features(), 合并(merge)多链特征,输出 Template Feature,即:

[CL] template features, template_aatype : (4, 1102)
[CL] template features, template_all_atom_positions : (4, 1102, 37, 3)
[CL] template features, template_all_atom_mask : (4, 1102, 37)

注意 template_all_atom_mask,即 37 个原子的 mask 信息。

其中,37 个原子(Atom):

{'N': 0, 'CA': 1, 'C': 2, 'CB': 3, 'O': 4, 'CG': 5, 'CG1': 6, 'CG2': 7, 'OG': 8, 'OG1': 9, 'SG': 10, 
'CD': 11, 'CD1': 12, 'CD2': 13, 'ND1': 14, 'ND2': 15, 'OD1': 16, 'OD2': 17, 'SD': 18, 'CE': 19, 'CE1': 20, 
'CE2': 21, 'CE3': 22, 'NE': 23, 'NE1': 24, 'NE2': 25, 'OE1': 26, 'OE2': 27, 'CH2': 28, 'NH1': 29, 'NH2': 30, 
'OH': 31, 'CZ': 32, 'CZ2': 33, 'CZ3': 34, 'NZ': 35, 'OXT': 36}

backbone_mask 特征,只关注 N、CA、C 三类原子的 Mask 信息,参考:

  • 一般情况下都与 pseudo_beta_mask 相同,因为要么存在残基,要么不存在残基。
# openfold/utils/all_atom_multimer.py
def make_backbone_affine(
    positions: geometry.Vec3Array,
    mask: torch.Tensor,
    aatype: torch.Tensor,
) -> Tuple[geometry.Rigid3Array, torch.Tensor]:
    a = rc.atom_order["N"]
    b = rc.atom_order["CA"]
    c = rc.atom_order["C"]

    rigid_mask = mask[..., a] * mask[..., b] * mask[..., c]

    rigid = make_transform_from_reference(
        a_xyz=positions[..., a],
        b_xyz=positions[..., b],
        c_xyz=positions[..., c],
    )

    return rigid, rigid_mask

pseudo_beta_maskbackbone_mask 相同,[1, 1102],即:

mask


4. multichain_mask_2d 特征

非常简单,就是链内 Mask,源码:

# openfold/model/model.py
multichain_mask_2d = (
    asym_id[..., None] == asym_id[..., None, :]
)  # [N_res, N_res]

multichain_mask_2d[1102, 1102],即:

multichain_mask_2d


5. unit_vector 特征

unit_vectorRot3Array 对象,与角度相关的单位向量,源码:

# openfold/model/embedders.py
rigid, backbone_mask = all_atom_multimer.make_backbone_affine(
    atom_pos,
    single_template_feats["template_all_atom_mask"],
    single_template_feats["template_aatype"],
)
points = rigid.translation
rigid_vec = rigid[..., None].inverse().apply_to_point(points)
unit_vector = rigid_vec.normalized()

# openfold/utils/all_atom_multimer.py
def make_backbone_affine(
    positions: geometry.Vec3Array,
    mask: torch.Tensor,
    aatype: torch.Tensor,
) -> Tuple[geometry.Rigid3Array, torch.Tensor]:
    a = rc.atom_order["N"]
    b = rc.atom_order["CA"]
    c = rc.atom_order["C"]

    rigid_mask = mask[..., a] * mask[..., b] * mask[..., c]

    rigid = make_transform_from_reference(
        a_xyz=positions[..., a],
        b_xyz=positions[..., b],
        c_xyz=positions[..., c],
    )

    return rigid, rigid_mask

# openfold/utils/all_atom_multimer.py
def make_transform_from_reference(
    a_xyz: geometry.Vec3Array, b_xyz: geometry.Vec3Array, c_xyz: geometry.Vec3Array
) -> geometry.Rigid3Array:
    """Returns rotation and translation matrices to convert from reference.

    Note that this method does not take care of symmetries. If you provide the
    coordinates in the non-standard way, the A atom will end up in the negative
    y-axis rather than in the positive y-axis. You need to take care of such
    cases in your code.

    Args:
        a_xyz: A Vec3Array.
        b_xyz: A Vec3Array.
        c_xyz: A Vec3Array.

    Returns:
        A Rigid3Array which, when applied to coordinates in a canonicalized
        reference frame, will give coordinates approximately equal
        the original coordinates (in the global frame).
    """
    rotation = geometry.Rot3Array.from_two_vectors(c_xyz - b_xyz, a_xyz - b_xyz)
    return geometry.Rigid3Array(rotation, b_xyz)

# openfold/utils/geometry/rotation_matrix.py
@classmethod
def from_two_vectors(cls, e0: vector.Vec3Array, e1: vector.Vec3Array) -> Rot3Array:
    """Construct Rot3Array from two Vectors.

    Rot3Array is constructed such that in the corresponding frame 'e0' lies on
    the positive x-Axis and 'e1' lies in the xy plane with positive sign of y.

    Args:
        e0: Vector
        e1: Vector
    Returns:
        Rot3Array
    """
    # Normalize the unit vector for the x-axis, e0.
    e0 = e0.normalized()
    # make e1 perpendicular to e0.
    c = e1.dot(e0)
    e1 = (e1 - c * e0).normalized()
    # Compute e2 as cross product of e0 and e1.
    e2 = e0.cross(e1)
    return cls(e0.x, e1.x, e2.x, e0.y, e1.y, e2.y, e0.z, e1.z, e2.z)

解释:每个残基的局部框架内所有残基的 α \alpha α 碳原子位移的单位向量。 这些局部框架的计算方式与目标结构相同。

The unit vector of the displacement of the alpha carbon atom of all residues within the local frame of each residue. These local frames are computed in the same way as for the target structure.

计算逻辑:

Rigid

unit_vector 包括x、y、z等3个分量,[1, 1102, 1102, 3],即:

unit_vector


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1192352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从硬件“卷”到UI交互,车企怎样才能掌握智能化「灵魂」

随着汽车智能化为座舱交互带来的超越传统汽车的感知能力和算力&#xff0c;车企在视觉体验设计&#xff08;包括仪表、车机、HUD的UI设计以及HMI相关业务模块&#xff0c;比如智驾视觉交互&#xff09;的布局&#xff0c;正在进入新周期。 与此同时&#xff0c;交互逻辑和UI设计…

基于SSM的食用菌菌棒溯源系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

APP安全测试详解

在工作过程中&#xff0c;我接触到了一些SDL安全提测的工作。原来我是学web端渗透比较多的&#xff0c;移动端这块基本没怎么试过手&#xff0c;结果刚开始一直踩坑&#xff0c;连抓包都抓不到(&#xff34;▽&#xff34;)。 下面记录下我遇到的部分问题和解决方法&#xff0…

史上最细,Jenkins插件Allure生成自动化测试报告详细...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、Allure介绍 A…

媒体转码软件Media Encoder 2024 mac中文版功能介绍

Media Encoder 2024 mac是一款媒体转码软件&#xff0c;它可以将视频从一种格式转码为另一种格式&#xff0c;支持H.265、HDR10等多种编码格式&#xff0c;同时优化了视频质量&#xff0c;提高了编码速度。此外&#xff0c;Media Encoder 2024还支持收录、创建代理和输出各种格…

Presto资源管理之Resource Groups And Selector

文章目录 前言资源组配置选择器规则 Selector Rules全局配置 Global Properties选择器属性配置案例配置 prestoDb 前言 资源组对资源使用进行限制&#xff0c;并可以对在其中运行的查询执行队列策略&#xff0c;或将资源分配给子组。查询属于单个资源组&#xff0c;并且从该组…

uniapp+vue3+ts+vite+echarts开发图表类小程序,将echarts导入项目使用的详细步骤,耗时一天终于弄好了

想在uniapp和vue3环境中使用echarts是一件相当前卫的事情&#xff0c;官方适配的还不是很好&#xff0c;echarts的使用插件写的是有些不太清晰的&#xff0c;这里我花费了一天的时间&#xff0c;终于将这个使用步骤搞清楚了&#xff0c;并且建了一个仓库&#xff0c;大家可以直…

工业自动化与物联网技术的融合:开启智能制造新时代

工业自动化与物联网技术的融合&#xff1a;开启智能制造新时代 随着科技的飞速发展&#xff0c;工业自动化与物联网技术的融合已经成为现代制造业的重要发展趋势。本文将分析工业自动化与物联网技术的关系、应用场景以及面临的挑战&#xff0c;并展望未来的发展趋势。 一、工业…

基于element-plus定义表格行内编辑配置化

文章目录 前言一、新增table组件二、使用步骤 前言 在 基于element-plus定义表单配置化 基础上&#xff0c;封装个Element-plus的table表格 由于表格不同于form组件&#xff0c;需自定义校验器&#xff0c;以下组件配置了单个校验&#xff0c;及提交统一校验方法&#xff0c;且…

【vue会员管理系统】篇六之退出系统功能

一、效果图 点击之后跳转到登陆界面 二、实现步骤 2.1Easy Mock新增接口 打开Easy Mock新建接口 方法:post URL:user/logout 描述&#xff1a;退出系统 2.2新增api 在api/login.js下添加以下代码 export function logout(token) {return request({url: /user/logout,method:…

【2021研电赛】管道巡检机器人

本作品介绍参与极术社区的有奖征集|分享研电赛作品扩大影响力&#xff0c;更有重磅电子产品免费领取! 团队介绍 参赛单位&#xff1a;广西科技大学 参赛队伍&#xff1a;OMEN 参赛队员&#xff1a;吴海晨 陈永亮 乔亚坤 第1章 项目意义 1.1 研究背景及意义 据媒体报道&am…

NIO 笔记(二)Netty框架专题

【笔记来自&#xff1a;it白马】 Netty框架 前面我们学习了Java为我们提供的NIO框架&#xff0c;提供使用NIO提供的三大组件&#xff0c;我们就可以编写更加高性能的客户端/服务端网络程序了&#xff0c;甚至还可以自行规定一种通信协议进行通信。 NIO框架存在的问题 但是之…

全国消费者行为和购买力的大数据可视化动态大屏【可视化项目案例-01】

🎉🎊🎉 你的技术旅程将在这里启航! 🚀🚀 本文选自专栏:可视化技术专栏100例 可视化技术专栏100例,包括但不限于大屏可视化、图表可视化等等。订阅专栏用户在文章底部可下载对应案例源码以供大家深入的学习研究。 🎓 每一个案例都会提供完整代码和详细的讲解,不…

瑞芯微:基于RK3568的深度估计模型部署

根据单张图像估计深度信息是计算机视觉领域的经典问题&#xff0c;也是一项具有挑战的难题。由于单目图像的尺度不确定&#xff0c;传统方法无法计算深度值。 随着深度学习技术的发展&#xff0c;该范式已经成为了估计单目图像的深度信息的一种解决方案。早期的深度估计方法大多…

InSAR数据处理、地形三维重建、形变信息提取、监测丨GMTSAR合成孔径雷达干涉测量丨GNSS、北斗高精度数据处理

目录 ①合成孔径雷达干涉测量InSAR数据处理、地形三维重建、形变信息提取、监测等应用 ②基于GMTSAR合成孔径雷达干涉测量InSAR数据处理、形变信息提取与分析 ③GNSS、北斗高精度数据处理暨新版GAMITGLOBK软件应用 更多应用 ①合成孔径雷达干涉测量InSAR数据处理、地形三维…

sCrypt 现在支持 Ordinals 了

比特币社区对 1Sat Ordinals 的接受度正在迅速增加&#xff0c;已有超过 4800 万个铭文被铸造&#xff0c;这一新创新令人兴奋不已。 尽管令人兴奋&#xff0c;但 Ordinals 铭文的工具仍然不发达&#xff0c;这使得使用 Ordinals 进行构建具有挑战性。 更具体地说&#xff0c;缺…

智慧城市照明为城市节能降耗提供支持继电器开关钡铼S270

智慧城市照明&#xff1a;为城市节能降耗提供支持——以钡铼技术S270继电器开关为例 随着城市化进程的加速&#xff0c;城市照明系统的需求也日益增长。与此同时&#xff0c;能源消耗和环境污染问题日益严重&#xff0c;使得城市照明的节能减排成为重要议题。智慧城市照明系统…

MapBox免Token离线部署全套成熟方案

看了网上的一些Mapbox离线部署使用的文章,有些讲述的不是很全面,有些简直就是跟风胡说。 这篇文章我们来给大家提供一个完整的成熟的mapbox-gl离线部署方案。 首先我们要搞清楚为什么mapbox-gl需要申请token才能使用,其实并不是框架本身的源码不想让大家用,否则mapbox也不…

微软宣布称Windows 再不会偷偷下载更新文件,真的吗?

导读时钟拨回到2015年&#xff0c;微软刚刚推出Windows 10操作系统时&#xff0c;一些Windows 7用户首次在线Update的升级文件大小居然高达6~8GB。这件事引发了大量的不满&#xff0c;一些按照流量计费和宽带不给力的用户怨言极为严重&#xff0c;其中德国用户把此事闹上了当地…

使用小程序插件【用户信息功能页】获取用户昵称、头像、openid

摘要 因为获取用户信息的接口 wx.getUserInfo 存在滥用&#xff0c;使用不规范等原因&#xff0c;微信官方已经将这个接口权限回收&#xff0c;改为用户信息填写&#xff0c;即引导用户主动填写微信昵称和上传头像。这种做法确实是麻烦了点。 但是微信小程序插件&#xff0c;…