Simon Tatham's Coroutines in C

【AI方案集】AI智能体全自动跨境电商运营系统设计.docx 这套AI技术方案不走“先堆概念再硬套场景”的老路,而是以业务痛点为锚点,把机器视觉识别、预测性分析、大模型知识赋能等能力拆解成轻量化可落地的功能模块,直接嵌入生产运营的关键环节,既能快速落地见效,又能通过持续的数据迭代逐步放大价值,帮企业在不盲目新增硬件投入的前提下,稳步完成智能化升级,实实在在把AI变成能降本、提效、控风险的生产工具。 立即下载

Coroutines in C

by Simon Tatham

Introduction

Structuring a large program is always a difficult job. One of the particular problems that often comes up is this: if you have a piece of code producing data, and another piece of code consuming it, which should be the caller and which should be the callee?

Here is a very simple piece of run-length decompression code, and an equally simple piece of parser code:

    /* Decompression code */
while (1) {
c = getchar();
if (c == EOF)
break;
if (c == 0xFF) {
len = getchar();
c = getchar();
while (len--)
emit(c);
} else
emit(c);
}
emit(EOF);
    /* Parser code */
while (1) {
c = getchar();
if (c == EOF)
break;
if (isalpha(c)) {
do {
add_to_token(c);
c = getchar();
} while (isalpha(c));
got_token(WORD);
}
add_to_token(c);
got_token(PUNCT);
}

Each of these code fragments is very simple, and easy to read and understand. One produces a character at a time by calling emit() ; the other consumes a character at a time by calling getchar() . If only the calls to emit() and the calls to getchar() could be made to feed data to each other, it would be simple to connect the two fragments together so that the output from the decompressor went straight to the parser.

In many modern operating systems, you could do this using pipes between two processes or two threads. emit() in the decompressor writes to a pipe, and getchar() in the parser reads from the other end of the same pipe. Simple and robust, but also heavyweight and not portable. Typically you don't want to have to divide your program into threads for a task this simple.

In this article I offer a creative solution to this sort of structure problem.

Rewriting

The conventional answer is to rewrite one of the ends of the communication channel so that it's a function that can be called. Here's an example of what that might mean for each of the example fragments.

int decompressor(void) {
static int repchar;
static int replen;
if (replen > 0) {
replen--;
return repchar;
}
c = getchar();
if (c == EOF)
return EOF;
if (c == 0xFF) {
replen = getchar();
repchar = getchar();
replen--;
return repchar;
} else
return c;
}
void parser(int c) {
static enum {
START, IN_WORD
} state;
switch (state) {
case IN_WORD:
if (isalpha(c)) {
add_to_token(c);
return;
}
got_token(WORD);
state = START;
/* fall through */

case START:
add_to_token(c);
if (isalpha(c))
state = IN_WORD;
else
got_token(PUNCT);
break;
}
}

Of course you don't have to rewrite both of them; just one will do. If you rewrite the decompressor in the form shown, so that it returns one character every time it's called, then the original parser code can replace calls to getchar() with calls to decompressor() , and the program will be happy. Conversely, if you rewrite the parser in the form shown, so that it is called once for every input character, then the original decompression code can call parser() instead of emit() with no problems. You would only want to rewrite both functions as callees if you were a glutton for punishment.

And that's the point, really. Both these rewritten functions are thoroughly ugly compared to their originals. Both of the processes taking place here are easier to read when written as a caller, not as a callee. Try to deduce the grammar recognised by the parser, or the compressed data format understood by the decompressor, just by reading the code, and you will find that both the originals are clear and both the rewritten forms are less clear. It would be much nicer if we didn't have to turn either piece of code inside out.

Knuth's coroutines

In The Art of Computer Programming , Donald Knuth presents a solution to this sort of problem. His answer is to throw away the stack concept completely. Stop thinking of one process as the caller and the other as the callee, and start thinking of them as cooperating equals.

In practical terms: replace the traditional "call" primitive with a slightly different one. The new "call" will save the return value somewhere other than on the stack, and will then jump to a location specified in another saved return value. So each time the decompressor emits another character, it saves its program counter and jumps to the last known location within the parser - and each time the parser needs another character, it saves its own program counter and jumps to the location saved by the decompressor. Control shuttles back and forth between the two routines exactly as often as necessary.

This is very nice in theory, but in practice you can only do it in assembly language, because no commonly used high level language supports the coroutine call primitive. Languages like C depend utterly on their stack-based structure, so whenever control passes from any function to any other, one must be the caller and the other must be the callee. So if you want to write portable code, this technique is at least as impractical as the Unix pipe solution.

Stack-based coroutines

So what we would really like is the ability to mimic Knuth's coroutine call primitive in C. We must accept that in reality, at the C level, one function will be caller and the other will be callee. In the caller, we have no problem; we code the original algorithm, pretty much exactly as written, and whenever it has (or needs) a character it calls the other function.

The callee has all the problems. For our callee, we want a function which has a "return and continue" operation: return from the function, and next time it is called, resume control from just after the return statement. For example, we would like to be able to write a function that says

 

int function(void) {
int i;
for (i = 0; i < 10; i++)
return i; /* won't work, but wouldn't it be nice */
}

and have ten successive calls to the function return the numbers 0 through 9.

How can we implement this? Well, we can transfer control to an arbitrary point in the function using a goto statement. So if we use a state variable, we could do this:

 

int function(void) {
static int i, state = 0;
switch (state) {
case 0: goto LABEL0;
case 1: goto LABEL1;
}
LABEL0: /* start of function */
for (i = 0; i < 10; i++) {
state = 1; /* so we will come back to LABEL1 */
return i;
LABEL1:; /* resume control straight after the return */
}
}

This method works. We have a set of labels at the points where we might need to resume control: one at the start, and one just after each return statement. We have a state variable, preserved between calls to the function, which tells us which label we need to resume control at next. Before any return, we update the state variable to point at the right label; after any call, we do a switch on the value of the variable to find out where to jump to.

It's still ugly, though. The worst part of it is that the set of labels must be maintained manually, and must be consistent between the function body and the initial switch statement. Every time we add a new return statement, we must invent a new label name and add it to the list in the switch ; every time we remove a return statement, we must remove its corresponding label. We've just increased our maintenance workload by a factor of two.

Duff's device

The famous "Duff's device" in C makes use of the fact that a case statement is still legal within a sub-block of its matching switch statement. Tom Duff used this for an optimised output loop:

 

    switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while ((count -= 8) > 0);
}

We can put it to a slightly different use in the coroutine trick. Instead of using a switch statement to decide which goto statement to execute, we can use the switch statement to perform the jump itself:

 

int function(void) {
static int i, state = 0;
switch (state) {
case 0: /* start of function */
for (i = 0; i < 10; i++) {
state = 1; /* so we will come back to "case 1" */
return i;
case 1:; /* resume control straight after the return */
}
}
}

Now this is looking promising. All we have to do now is construct a few well chosen macros, and we can hide the gory details in something plausible-looking:

 

#define crBegin static int state=0; switch(state) { case 0:
#define crReturn(i,x) do { state=i; return x; case i:; } while (0)
#define crFinish }
int function(void) {
static int i;
crBegin;
for (i = 0; i < 10; i++)
crReturn(1, i);
crFinish;
}

(note the use of do ... while(0) to ensure that crReturn does not need braces around it when it comes directly between if and else )

This is almost exactly what we wanted. We can use crReturn to return from the function in such a way that control at the next call resumes just after the return. Of course we must obey some ground rules (surround the function body with crBegin and crFinish ; declare all local variables static if they need to be preserved across a crReturn ; never put a crReturn within an explicit switch statement); but those do not limit us very much.

The only snag remaining is the first parameter to crReturn . Just as when we invented a new label in the previous section we had to avoid it colliding with existing label names, now we must ensure all our state parameters to crReturn are different. The consequences will be fairly benign - the compiler will catch it and not let it do horrible things at run time - but we still need to avoid doing it.

Even this can be solved. ANSI C provides the special macro name __LINE__ , which expands to the current source line number. So we can rewrite crReturn as

 

#define crReturn(x) do { state=__LINE__; return x; /
case __LINE__:; } while (0)

and then we no longer have to worry about those state parameters at all, provided we obey a fourth ground rule (never put two crReturn statements on the same line).

Evaluation

So now we have this monstrosity, let's rewrite our original code fragments using it.

int decompressor(void) {
static int c, len;
crBegin;
while (1) {
c = getchar();
if (c == EOF)
break;
if (c == 0xFF) {
len = getchar();
c = getchar();
while (len--)
crReturn(c);
} else
crReturn(c);
}
crReturn(EOF);
crFinish;
}
void parser(int c) {
crBegin;
while (1) {
/* first char already in c */
if (c == EOF)
break;
if (isalpha(c)) {
do {
add_to_token(c);
crReturn( );
} while (isalpha(c));
got_token(WORD);
}
add_to_token(c);
got_token(PUNCT);
crReturn( );
}
crFinish;
}

We have rewritten both decompressor and parser as callees, with no need at all for the massive restructuring we had to do last time we did this. The structure of each function exactly mirrors the structure of its original form. A reader can deduce the grammar recognised by the parser, or the compressed data format used by the decompressor, far more easily than by reading the obscure state-machine code. The control flow is intuitive once you have wrapped your mind around the new format: when the decompressor has a character, it passes it back to the caller with crReturn and waits to be called again when another character is required. When the parser needs another character, it returns using crReturn , and waits to be called again with the new character in the parameter c .

There has been one small structural alteration to the code: parser() now has its getchar() (well, the corresponding crReturn ) at the end of the loop instead of the start, because the first character is already in c when the function is entered. We could accept this small change in structure, or if we really felt strongly about it we could specify that parser() required an "initialisation" call before you could start feeding it characters.

As before, of course, we don't have to rewrite both routines using the coroutine macros. One will suffice; the other can be its caller.

We have achieved what we set out to achieve: a portable ANSI C means of passing data between a producer and a consumer without the need to rewrite one as an explicit state machine. We have done this by combining the C preprocessor with a little-used feature of the switch statement to create an implicit state machine.

Coding Standards

Of course, this trick violates every coding standard in the book. Try doing this in your company's code and you will probably be subject to a stern telling off if not disciplinary action! You have embedded unmatched braces in macros, used case within sub-blocks, and as for the crReturn macro with its terrifyingly disruptive contents . . . It's a wonder you haven't been fired on the spot for such irresponsible coding practice. You should be ashamed of yourself.

I would claim that the coding standards are at fault here. The examples I've shown in this article are not very long, not very complicated, and still just about comprehensible when rewritten as state machines. But as the functions get longer, the degree of rewriting required becomes greater and the loss of clarity becomes much, much worse.

Consider. A function built of small blocks of the form

 

    case STATE1:
/* perform some activity */
if (condition) state = STATE2; else state = STATE3;

is not very different, to a reader, from a function built of small blocks of the form

 

    LABEL1:
/* perform some activity */
if (condition) goto LABEL2; else goto LABEL3;

One is caller and the other is callee, true, but the visual structure of the functions are the same, and the insights they provide into their underlying algorithms are exactly as small as each other. The same people who would fire you for using my coroutine macros would fire you just as loudly for building a function out of small blocks connected by goto statements! And this time they would be right, because laying out a function like that obscures the structure of the algorithm horribly.

Coding standards aim for clarity. By hiding vital things like switch , return and case statements inside "obfuscating" macros, the coding standards would claim you have obscured the syntactic structure of the program, and violated the requirement for clarity. But you have done so in the cause of revealing the algorithmic structure of the program, which is far more likely to be what the reader wants to know!

Any coding standard which insists on syntactic clarity at the expense of algorithmic clarity should be rewritten. If your employer fires you for using this trick, tell them that repeatedly as the security staff drag you out of the building.

Refinements and Code

In a serious application, this toy coroutine implementation is unlikely to be useful, because it relies on static variables and so it fails to be re-entrant or multi-threadable. Ideally, in a real application, you would want to be able to call the same function in several different contexts, and at each call in a given context, have control resume just after the last return in the same context.

This is easily enough done. We arrange an extra function parameter, which is a pointer to a context structure; we declare all our local state, and our coroutine state variable, as elements of that structure.

It's a little bit ugly, because suddenly you have to use ctx->i as a loop counter where you would previously just have used i ; virtually all your serious variables become elements of the coroutine context structure. But it removes the problems with re-entrancy, and still hasn't impacted the structure of the routine.

(Of course, if C only had Pascal's with statement, we could arrange for the macros to make this layer of indirection truly transparent as well. A pity. Still, at least C++ users can manage this by having their coroutine be a class member, and keeping all its local variables in the class so that the scoping is implicit.)

Included here is a C header file that implements this coroutine trick as a set of pre-defined macros. There are two sets of macros defined in the file, prefixed scr and ccr . The scr macros are the simple form of the technique, for when you can get away with using static variables; the ccr macros provide the advanced re-entrant form. Full documentation is given in a comment in the header file itself.

Note that Visual C++ version 6 doesn't like this coroutine trick, because its default debug state (Program Database for Edit and Continue) does something strange to the __LINE__ macro. To compile a coroutine-using program with VC++ 6, you must turn off Edit and Continue. (In the project settings, go to the "C/C++" tab, category "General", setting "Debug info". Select any option other than "Program Database for Edit and Continue".)

(The header file is MIT-licensed, so you can use it in anything you like without restriction. If you do find something the MIT licence doesn't permit you to do, mail me , and I'll probably give you explicit permission to do it anyway.)

Follow this link for coroutine.h .

Thanks for reading. Share and enjoy!

产业园区运营负责人如何实现创新资源的系统化配置?.docx 产业园区运营负责人如何实现创新资源的系统化配置? 立即下载

相关推荐

高校技术转移办公室人员需要哪些材料完成成果转化项目审核?.docx

高校技术转移办公室人员需要哪些材料完成成果转化项目审核?

Coroutine in C

0. 前言 以下介绍的两个大牛都是利用了 switch 语句中的 case 部分可以置于一个 block (通常是 if{} 或 while{} 块)中, switch 语句执行一个直接跳转进入该 block,进入该 block 的条件有时候被有意地忽略。 全文思路和代码绝无原创之处,只是略加整理了一下。更详细的请看提供的链接。 希望高手老鸟们指出错误不足并给出更深入的分析,如果能提出比例子更漂亮的方案 就太棒了(我感觉可以用回调函数一类的方法实现协作过程,不过还没细想)。 代码规范的讨论就请

skd2278的专栏 1165

科技中介服务机构如何降低服务门槛、提升客户粘性?.docx

科技中介服务机构如何降低服务门槛、提升客户粘性?

无源项一维波动问题的PINN的Python torch实现

内容概要:本文详细介绍了无源项一维波动问题的物理信息神经网络(PINN)的Python实现方法,基于PyTorch框架构建神经网络模型,将波动方程作为物理约束嵌入损失函数中,实现对偏微分方程的无网格数值求解。文中系统阐述了PINN的核心原理、网络结构设计、初始与边界条件的处理策略,以及利用自动微分技术高效计算高阶偏导数的方法。该方法摆脱了传统数值方法对网格划分的依赖,能够以端到端的方式逼近复杂物理问题的解,展现出在科学计算与工程仿真领域的强大潜力,尤其适用于高维或多物理场耦合问题的智能求解。; 适合人群:具备深度学习基础、偏微分方程知识及Python编程能力的研究生、科研人员,以及从事科学计算、工程仿真与人工智能交叉领域研究的专业技术人员。; 使用场景及目标:①掌握物理信息神经网络(PINN)求解偏微分方程的理论与实现流程;②完成无源项一维波动方程的神经网络建模、训练与结果可视化;③将该方法迁移应用于热传导、流体力学、电磁场等其他物理系统的建模与仿真研究; 其他说明:代码实现强调物理定律与数据驱动的深度融合,建议读者在学习过程中结合PyTorch框架特性与微分方程理论,通过调整网络层数、激活函数、损失权重和优化器参数等方式,探究不同配置对模型收敛速度与求解精度的影响,以深化对PINN工作机制的理解。

数字政府AI知识库平台规划方案(技术方案版).pptx

数字政府AI知识库平台规划方案(技术方案版).pptx

高校技术转移办公室人员如何更高效地进行成果推介书制作?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

工业流水线上玻璃瓶内异物检测数据集VOC+YOLO格式915张1类别.md

【重要提示】本页面仅为资源介绍,不含外部下载地址 亲爱的 优快云 用户: 感谢你浏览本资源页面,在这里提前向大家说明相关情况,避免后续产生误解。本资源我设置的初衷是 0 积分免费分享文档介绍,希望可以无偿给到有需要的朋友。但 优快云 平台存在自动调控机制,系统会根据资源下载热度、文件体积、平台规则等因素,自动修改资源所需下载积分,页面显示为 1 积分、2 积分或更高积分都属于平台自动调整结果,并非我手动修改设置,我没有权限自主锁定积分数值。 如果你打开页面后,发现下载所需积分不为 0,请务必谨慎考虑是否下载。一旦你支付积分完成下载,后续遇到资源内容和描述不符、文件损坏等问题,我无法承担对应的积分损失,也没有渠道为你办理积分退还。在此强烈建议,只有页面明确显示 0 积分的时候,再进行下载操作。 同时特别说明:这份资源介绍文案里,没有附带任何外部下载地址。资源本身是通过 优快云 官方上传通道上传的附件,正常情况下直接点击页面自带的下载按钮即可获取文件。页面找不到外部下载链接属于正常情况,并非资源缺失。 如果因为平台积分显示异常,导致你被扣取积分,积分退款、积分异常相关问题,请直接联系 优快云 官方客服咨询处理。平台规则由官方制定,我无法干预积分变动,望各位理解。

物流运输业AI知识库平台规划方案(决策评审版).pptx

物流运输业AI知识库平台规划方案(决策评审版).pptx

【网络性能测试】基于Iperf工具的Linux服务器带宽检测:TCP/UDP上下行速率与多并发稳定性评估方案

涵盖TCP和UDP协议下的上传与下载带宽、多并发连接支持以及长时间稳定性测试。详细说明了Iperf的安装步骤(包括内容概要:服务端CentOS系统本文介绍了如何和客户端Windows系统的使用Iperf工具部署)、基本命令对Linux服务器带用法及关键宽进行全方位测试参数解析,并提供了多种实际测试场景的命令,涵盖TCP和UDP协议下的上传示例,如指定端口、带与下载带宽、宽、时间、多并发连接支持能力以及网络稳定性测试。线程数等,帮助用户全面详细说明了Iperf的安装步骤评估网络服务质量。此外(包括服务端Cent,还列出了常用OS系统和客户端参数及其功能,便于Windows系统的部署)、基本灵活配置测试环境命令格式及常用。; 适合参数含义,并通过人群:具备一定具体命令示例演示Linux系统操作基础和网络了不同场景下的测试方法,如指定知识,从事运维、端口、带网络测试或系统宽、测试时管理相关工作的技术人员长、并发线,尤其是需要对程数等,服务器带宽性能帮助用户全面评估网络进行评估的专业人员。; 使用性能表现。;场景及目标: 适合人群:具备①测试服务器在基础网络知识和高负载下的最大吞Linux操作能力的运维吐能力;人员、网络工程师②验证网络链或开发人员,路是否存在丢包或尤其适用于需要对波动;③服务器网络性能进行评估多并发连接对调优或故障排查的技术人员。带宽的影响;; 使用场景及④进行长时间运行目标:①测试的稳定性监测,服务器在高负载下的最大吞吐能力确保生产环境网络可靠;②评估。; 阅读建议:建议读者TCP/UDP传输结合文档中的命令实例,在真实环境中搭建性能差异;③验证多线程并发连接对带测试场景,逐步实践宽的影响;④各项参数组合,长时间运行测试以重点关注测试结果中的判断网络稳定性;⑤带宽数值、丢包率和延迟变化诊断丢包、,从而深入理解网络延迟等问题。; 阅读建议:建议性能瓶颈所在,并在实际环境中搭建优化网络架构。测试环境,结合文档中的命令示例逐步实践,重点关注参数组合对测试结果的影响,并利用日志记录和分析功能提升测试准确性。

科技中介服务机构如何借助AI提升技术评估与服务推荐的专业性.docx

科技中介服务机构如何借助AI提升技术评估与服务推荐的专业性

零售行业AI知识库平台规划方案(技术方案版).pptx

零售行业AI知识库平台规划方案(技术方案版).pptx

智慧交通高速公路城市道路路面抛洒物散落货物障碍物检测数据集VOC+YOLO格式4521张1类别.md

【重要提示】本页面仅为资源介绍,不含外部下载地址 亲爱的 优快云 用户: 感谢你浏览本资源页面,在这里提前向大家说明相关情况,避免后续产生误解。本资源我设置的初衷是 0 积分免费分享文档介绍,希望可以无偿给到有需要的朋友。但 优快云 平台存在自动调控机制,系统会根据资源下载热度、文件体积、平台规则等因素,自动修改资源所需下载积分,页面显示为 1 积分、2 积分或更高积分都属于平台自动调整结果,并非我手动修改设置,我没有权限自主锁定积分数值。 如果你打开页面后,发现下载所需积分不为 0,请务必谨慎考虑是否下载。一旦你支付积分完成下载,后续遇到资源内容和描述不符、文件损坏等问题,我无法承担对应的积分损失,也没有渠道为你办理积分退还。在此强烈建议,只有页面明确显示 0 积分的时候,再进行下载操作。 同时特别说明:这份资源介绍文案里,没有附带任何外部下载地址。资源本身是通过 优快云 官方上传通道上传的附件,正常情况下直接点击页面自带的下载按钮即可获取文件。页面找不到外部下载链接属于正常情况,并非资源缺失。 如果因为平台积分显示异常,导致你被扣取积分,积分退款、积分异常相关问题,请直接联系 优快云 官方客服咨询处理。平台规则由官方制定,我无法干预积分变动,望各位理解。

环境(docker)部署若依 RuoYi‑Vue CICD 完整实操流程-实战

环境(docker)部署若依 RuoYi‑Vue CICD 完整实操流程-实战

全桥LLC谐振(变频+移相混合控制)变换器闭环仿真模型(Simulink仿真实现)

内容概要:本文详细阐述了全桥LLC谐振变换器在变频与移相混合控制策略下的闭环仿真模型构建与实现过程,重点聚焦于其在Simulink环境中的系统建模与先进控制方法设计。文章首先深入剖析了LLC谐振变换器的工作原理,突出其在高频、高效率电源转换应用中的显著优势。针对宽范围输入电压带来的挑战,提出了一种融合变频控制与移相控制的混合调制策略,以实现高效、稳定的输出电压调节。通过建立精确的数学模型,设计了包含电压外环与电流内环的双闭环控制系统,有效提升了系统的动态响应速度和稳态精度,并显著降低了输出电压纹波。研究利用Simulink平台完成了完整的系统仿真验证,并深入探讨了关键电路与控制参数对整体系统性能的影响,为实际工程应用中的设计优化提供了坚实的理论依据和技术支撑。; 适合人群:具备一定电力电子技术基础,熟悉Simulink仿真工具,从事电源变换器设计与研究的研发人员。; 使用场景及目标:①掌握全桥LLC谐振变换器在变频与移相混合控制下的工作机理;②学习如何在Simulink中构建复杂的电力电子系统仿真模型;③了解并应用双闭环控制策略提高变换器性能; 阅读建议:建议读者结合文中提供的仿真模型与参数设置,动手实践以加深理解,同时关注控制策略的实际效果与理论分析之间的差异,不断优化模型参数以达到最佳性能。

状态估计【扩展卡尔曼滤波算法的神经网络训练】BP神经网络、扩展卡尔曼滤波EKF+BP、粒子滤波PF轨迹估计研究(Matlab代码实现)

【状态估计】【扩展卡尔曼滤波算法的神经网络训练】BP神经网络、扩展卡尔曼滤波EKF+BP、粒子滤波PF轨迹估计研究(Matlab代码实现)内容概要:本文主要介绍了基于变频控制的多相交错LLC谐振变换器闭环仿真研究,重点探讨了多相交错并联技术在降低输出电流纹波和提升功率容量方面的优势,分析了交错角对纹波抵消的影响以及均流问题的成因。针对变频控制带来的非线性特性,文章采用扩展描述函数(EDF)建立小信号模型,用于指导PID控制器的设计,并验证其在稳态和动态工况下的控制性能。通过Simulink仿真平台对闭环系统进行建模与测试,考察了启动过程、负载阶跃响应及频率调节的平滑性,结果表明所设计的控制系统具有良好的稳态精度和动态响应能力。; 适合人群:电力电子、电气工程及其自动化等相关专业的高校师生及从事电源系统研发的工程技术人员。; 使用场景及目标:①掌握多相交错LLC谐振变换器的工作原理与建模方法;②学习变频控制下非线性系统的建模与PID环路设计技巧;③应用于高效率、低纹波开关电源的仿真设计与优化。; 阅读建议:建议结合文中提供的仿真模型与参数设置进行实践操作,重点关注EDF建模过程与控制器参数整定方法,通过对比不同交错角下的仿真结果加深对纹波抑制机理的理解。

论文复现基于物理信息神经网络预测2D3D非稳态温度场及热源(Python代码实现)

内容概要:本文详细介绍了基于物理信息神经网络(PINN)预测2D和3D非稳态温度场及热源的Python代码实现,旨在复现相关科研论文。该方法将物理定律嵌入神经网络结构中,利用偏微分方程描述传热过程,在无需大量标注数据的前提下,求解复杂的非稳态传热问题,实现对温度场时空演化和内部热源分布的高精度预测。文中系统阐述了PINN的模型构建原理、损失函数设计策略(包含初始条件、边界条件与控制方程的物理约束)、网络训练流程以及数值实验结果,充分展示了其在科学计算与工程仿真中的强大潜力与良好泛化能力。; 适合人群:具备一定Python编程基础和深度学习理论知识,熟悉偏微分方程与传热学基本概念,对科学计算、物理信息建模、智能仿真等方向感兴趣的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于复现并深入理解PINN在非稳态热传导问题中的具体应用;②为从事热管理、材料科学、能源系统、航空航天等领域的研究人员提供可扩展、可定制的开源代码框架;③辅助高校教学与科研实践,帮助学生掌握物理信息驱动的机器学习建模方法及其在实际工程问题中的实现路径。; 阅读建议:建议读者结合文中提供的完整代码与详细注释,使用主流深度学习框架(如TensorFlow或PyTorch)进行动手实践,重点理解物理约束如何通过损失项融入网络训练过程,并尝试在不同几何构型、边界条件或热源形式下进行模型验证,以深入掌握PINN的设计思想与泛化机制。

美股分钟级历史行情数据,涵盖纽交所、纳斯达克及部分ETF 每条数据均包含时间戳、开盘价、最高价、最低价、收盘价、成交量、成交额及前收盘价 时间粒度为1分钟和5分钟两种 数据起始时间可追溯至数年前,已做

美股分钟级历史行情数据,涵盖纽交所、纳斯达克及部分ETF。每条数据均包含时间戳、开盘价、最高价、最低价、收盘价、成交量、成交额及前收盘价。时间粒度为1分钟和5分钟两种。数据起始时间可追溯至数年前,已做复权处理,前复权与后复权价格均有提供。 数据源:CMES金融数据库

当迁移学习遇见室内定位:基于UJIIndoorLoc的跨场景适配实践

本文探讨了迁移学习在跨建筑室内定位中的应用,重点解析了UJIIndoorLoc数据集的特性及其在解决设备差异和环境变化中的关键作用。通过特征工程、迁移学习策略和小样本微调技术,显著提升了定位模型的跨场景适配能力,为实际部署提供了有效解决方案。

weixin_29038155的博客 394
上一篇: 程序堆栈
下一篇: 编译器链接详解zz
wy90021
博客等级 码龄20年 0粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值