交易中的神经网络:广义 3D 引用表达分段·进阶篇
(2/3)·传统3D-RES只能锁定单目标,真实指令常落空或撞上多对象,本篇拆解MDIN与TSQ的实战分工
接上篇对基础3D-RES的铺垫,我们继续深挖它在多目标与空指令场景下的崩塌点。真实盘口描述往往同时指向几个相似形态,或干脆没有对应结构,老方法直接失效。本篇把广义3D-GRES的模块拆开,看它怎么用多个查询并行救场。
「GRES 神经元的接口骨架与初始化参数」
在 MT5 的 OpenCL 神经网络扩展里,CNeuronGRES 类承担了一种带门控与参考记忆的复合层角色。它的头文件声明里大量使用了 override 虚函数,但其中 feedForward(CNeuronBaseOCL*) 与 calcInputGradients(CNeuronBaseOCL*) 等单输入版本直接返回 false,说明该类不走标准单层前向路径,必须配合 SecondInput 的双输入重载才真正生效。 看这段类声明里的关键虚函数: virtual bool DiversityLoss(CNeuronBaseOCL *neuron, const int units, const int dimension, const bool add = false); virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override { return false; } virtual bool feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override { return false; } virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput, CBufferFloat *SecondGradient, ENUM_ACTIVATION SecondActivation = None) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override { return false; } virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override; 其中 DiversityLoss 用于衡量不同神经元输出的多样性损失,units 与 dimension 控制参与计算的张量形状,add 参数决定是覆盖还是累加梯度。 Init 函数的参数表暴露了这类层对显存与并行结构的强依赖:window / window_key 定义主时间窗,units_count 与 heads 控制多头注意力式的拆分,而 window_sp / units_sp / heads_sp 及 layers_to_sp 则指向二级空间路径。ref_size 与 layers 决定参考缓冲与堆叠深度,optimization_type 与 batch 影响权重更新粒度。 在实盘模型里,若 heads 设得过高(例如 8 以上)而 batch 偏小,可能倾向出现显存分配失败;开 MT5 用 COpenCLMy 跑一次 Init 返回 false 时,优先查 window*units_count*heads 的乘积是否超出设备上限。
class="kw">virtual class="type">bool DiversityLoss(CNeuronBaseOCL *neuron, class="kw">const class="type">int units, class="kw">const class="type">int dimension, class="kw">const class="type">bool add = false); class=class="str">"cmt">//--- class="kw">virtual class="type">bool feedForward(CNeuronBaseOCL *NeuronOCL) class="kw">override { class="kw">return false; } class="kw">virtual class="type">bool feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) class="kw">override; class=class="str">"cmt">//--- class="kw">virtual class="type">bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) class="kw">override { class="kw">return false; } class="kw">virtual class="type">bool calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput, CBufferFloat *SecondGradient, ENUM_ACTIVATION SecondActivation = None) class="kw">override; class="kw">virtual class="type">bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) class="kw">override { class="kw">return false; } class="kw">virtual class="type">bool updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) class="kw">override; class="kw">public: CNeuronGRES(class="type">void) {}; ~CNeuronGRES(class="type">void) {}; class=class="str">"cmt">//--- class="kw">virtual class="type">bool Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl, class="type">uint window, class="type">uint window_key, class="type">uint units_count, class="type">uint heads, class="type">uint window_sp, class="type">uint units_sp, class="type">uint heads_sp, class="type">uint ref_size, class="type">uint layers, class="type">uint layers_to_sp, ENUM_OPTIMIZATION optimization_type, class="type">uint batch); class=class="str">"cmt">//--- class="kw">virtual class="type">int Type(class="type">void) class="kw">override class="kw">const { class="kw">return defNeuronGRES; } class=class="str">"cmt">//--- class="kw">virtual class="type">bool Save(class="type">int class="kw">const file_handle) class="kw">override; class="kw">virtual class="type">bool Load(class="type">int class="kw">const file_handle) class="kw">override; class=class="str">"cmt">//--- class="kw">virtual class="type">bool WeightsUpdate(CNeuronBaseOCL *source, class="type">float tau) class="kw">override; class="kw">virtual class="type">void SetOpenCL(COpenCLMy *obj) class="kw">override; };
多头注意力里的查询分支初始化
这段 Init 函数负责搭起自注意力模块中 Query 分支的底层拓扑。先调基类 CNeuronBaseOCL::Init,把展平后的输入维度 window*units_count 喂进去;任何一步返回 false 就直接退出,保证 OpenCL 资源没建半截。 iLayers 和 iLayersSP 都用 MathMax(layers,1) 与 MathMax(layers_to_sp,1) 兜底,意味着层数参数传 0 也不会崩,最少跑一层。 查询分支依次挂了转置、卷积、再转置、再卷积、可学习位置编码 PE,最后用 base 把 PE 输出接上。卷积层统一设 SIGMOID 激活,这是该分支的固定选择。 SuperPoints 段从 layer_id=6 起循环 4 次,当 iSPUnits 为偶数就减半并新建 CResidualConv,残差卷积窗口从 2*iSPWindow 压回 iSPWindow。若你改 iSPUnits 初始值为奇数,该分支会整层跳过减半逻辑,显存占用和感受野都会偏离预期——开 MT5 把 iSPUnits 设成 64 和 65 各跑一次能直接看出差别。 外汇与贵金属行情受杠杆与跳空影响大,这类 GPU 算子仅用于历史样本推理加速,实盘信号请自担高风险。
class="type">uint window_sp, class="type">uint units_sp, class="type">uint heads_sp, class="type">uint ref_size, class="type">uint layers, class="type">uint layers_to_sp, ENUM_OPTIMIZATION optimization_type, class="type">uint batch ) { if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window * units_count, optimization_type, batch)) class="kw">return false; iWindow = window; iUnits = units_count; iHeads = heads; iSPUnits = units_sp; iSPWindow = window_sp; iSPHeads = heads_sp; iWindowKey = window_key; iLayers = MathMax(layers, class="num">1); iLayersSP = MathMax(layers_to_sp, class="num">1); CNeuronBaseOCL *base = NULL; CNeuronTransposeOCL *transp = NULL; CNeuronConvOCL *conv = NULL; CNeuronLearnabledPE *pe = NULL; class=class="str">"cmt">//--- Init Querys cQuery.Clear(); transp = new CNeuronTransposeOCL(); if(!transp || !transp.Init(class="num">0, class="num">0, OpenCL, iSPUnits, iSPWindow, optimization, iBatch) || !cQuery.Add(transp)) class="kw">return false; conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, class="num">1, OpenCL, iSPUnits, iSPUnits, iUnits, class="num">1, iSPWindow, optimization, iBatch) || !cQuery.Add(conv)) class="kw">return false; conv.SetActivationFunction(SIGMOID); transp = new CNeuronTransposeOCL(); if(!transp || !transp.Init(class="num">0, class="num">2, OpenCL, iSPWindow, iUnits, optimization, iBatch) || !cQuery.Add(transp)) class="kw">return false; conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, class="num">3, OpenCL, iSPWindow, iSPWindow, iWindow, iUnits, class="num">1, optimization, iBatch) || !cQuery.Add(conv)) class="kw">return false; conv.SetActivationFunction(SIGMOID); pe = new CNeuronLearnabledPE(); if(!pe || !pe.Init(class="num">0, class="num">4, OpenCL, iWindow * iUnits, optimization, iBatch) || !cQuery.Add(pe)) class="kw">return false; base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, class="num">5, OpenCL, pe.Neurons(), optimization, iBatch) || !base.SetOutput(pe.GetPE()) || !cQPosition.Add(base)) class="kw">return false; class=class="str">"cmt">//--- Init SuperPoints class="type">int layer_id = class="num">6; cSuperPoints.Clear(); for(class="type">int r = class="num">0; r < class="num">4; r++) { if(iSPUnits % class="num">2 == class="num">0) { iSPUnits /= class="num">2; CResidualConv *residual = new CResidualConv(); if(!residual || !residual.Init(class="num">0, layer_id, OpenCL, class="num">2 * iSPWindow, iSPWindow, iSPUnits, optimization, iBatch) ||
◍ 卷积与注意力层的堆叠逻辑
这段初始化代码展示了在 MT5 的 OpenCL 环境里,如何把卷积层、位置编码层和交叉注意力层逐层挂到容器上。每一层都靠 layer_id 自增来标记顺序,只要任意一个 Init 或 Add 返回失败,整个构建过程直接 return false,意味着模型结构在运行时可能不完整。 从循环体看,iLayers 控制总层数,而 l % iLayersSP == 0 决定何时插入 SuperPoint 分支的 Key 卷积——这说明并非每层都带 SP 头,而是按固定间隔复用。iWindowKey*iHeads 与 iWindowKey*iSPHeads 的维度差异,直接对应多头与 SP 多头的通道数分裂。 在 MT5 里验证时,可把 iLayersSP 从 2 改成 1,观察 cSuperPoints 容器长度是否翻倍;外汇与贵金属行情下用此类结构做推理属高风险,过拟合概率倾向偏高。
!cSuperPoints.Add(residual)) class="kw">return false; } else { iSPUnits--; conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, class="num">2 * iSPWindow, iSPWindow, iSPWindow, iSPUnits, class="num">1, optimization, iBatch) || !cSuperPoints.Add(conv)) class="kw">return false; conv.SetActivationFunction(SIGMOID); } layer_id++; } conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, iSPWindow, iSPWindow, iWindow, iSPUnits, class="num">1, optimization, iBatch) || !cSuperPoints.Add(conv)) class="kw">return false; conv.SetActivationFunction(SIGMOID); layer_id++; pe = new CNeuronLearnabledPE(); if(!pe || !pe.Init(class="num">0, layer_id, OpenCL, conv.Neurons(), optimization, iBatch) || !cSuperPoints.Add(pe)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Reference cReference.Clear(); base = new CNeuronBaseOCL(); if(!base || !base.Init(iWindow * iUnits, layer_id, OpenCL, ref_size, optimization, iBatch) || !cReference.Add(base)) class="kw">return false; layer_id++; base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, layer_id, OpenCL, iWindow * iUnits, optimization, iBatch) || !cReference.Add(base)) class="kw">return false; base.SetActivationFunction(SIGMOID); layer_id++; pe = new CNeuronLearnabledPE(); if(!pe || !pe.Init(class="num">0, layer_id, OpenCL, base.Neurons(), optimization, iBatch) || !cReference.Add(pe)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Inside layers cQKey.Clear(); cQValue.Clear(); cSPKey.Clear(); cSPValue.Clear(); cSelfAttentionOut.Clear(); cCrossAttentionOut.Clear(); cMHCrossAttentionOut.Clear(); cMHSelfAttentionOut.Clear(); cMHRefAttentionOut.Clear(); cRefAttentionOut.Clear(); cRefKey.Clear(); cRefValue.Clear(); cResidual.Clear(); for(class="type">uint l = class="num">0; l < iLayers; l++) { class=class="str">"cmt">//--- Cross-Attention class=class="str">"cmt">//--- Query conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0,layer_id, OpenCL, iWindow, iWindow, iWindowKey*iHeads, iUnits, class="num">1,optimization, iBatch) || !cQuery.Add(conv)) class="kw">return false; layer_id++; if(l % iLayersSP == class="num">0) { class=class="str">"cmt">//--- Key conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, iWindow, iWindow, iWindowKey * iSPHeads, iSPUnits, class="num">1, optimization, iBatch) ||
「多头注意力层的栈式组装」
这段初始化逻辑把交叉注意力和自注意力两套多头结构,按 layer_id 自增顺序逐层挂到对应容器里。每一层都先 new 一个 CNeuronConvOCL 或 CNeuronBaseOCL,Init 失败就直接 return false,保证整图构建的原子性。 交叉注意力部分先铺 Key、Value 两个卷积层,窗口维度用 iWindowKey * iSPHeads 做输入通道,输出压到 iSPUnits;随后 Multy-Heads Attention Out 用 CNeuronBaseOCL 把 iWindowKey * iHeads * iUnits 维特征做线性融合。 Cross-Attention Out 再接一个卷积把维度收拢回 iWindowKey * iHeads,Residual 层则单独开 iWindow * iUnits 的 base 单元承接残差。自注意力段对称地建 Query / Key / Value 三个卷积,输入窗口都是 iWindow、通道数 iWindowKey*iHeads、单元数 iUnits,最后同样用 base 做多头输出融合。 在 MT5 里跑这类网络时,layer_id 的连续自增是关键:若你改了 iHeads 或 iWindowKey,必须确认容器 Add 顺序和维度参数同步,否则 Init 返回 false 会让整个模型静默构建失败。外汇与贵金属行情下用此类结构做推理属高风险,信号仅作概率参考。
!cSPKey.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Value conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, iWindow, iWindow, iWindowKey * iSPHeads, iSPUnits, class="num">1, optimization, iBatch) || !cSPValue.Add(conv)) class="kw">return false; layer_id++; } class=class="str">"cmt">//--- Multy-Heads Attention Out base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, layer_id, OpenCL, iWindowKey * iHeads * iUnits, optimization, iBatch) || !cMHCrossAttentionOut.Add(base)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Cross-Attention Out conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, iWindowKey * iHeads, iWindowKey * iHeads, iWindow, iUnits, class="num">1, optimization, iBatch) || !cCrossAttentionOut.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Residual base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, layer_id, OpenCL, iWindow * iUnits, optimization, iBatch) || !cResidual.Add(base)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Self-Attention class=class="str">"cmt">//--- Query conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0,layer_id, OpenCL, iWindow, iWindow, iWindowKey*iHeads, iUnits, class="num">1, optimization, iBatch) || !cQuery.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Key conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0,layer_id, OpenCL, iWindow, iWindow, iWindowKey*iHeads, iUnits, class="num">1, optimization, iBatch) || !cQKey.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Value conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0,layer_id, OpenCL, iWindow, iWindow, iWindowKey*iHeads, iUnits, class="num">1, optimization, iBatch) || !cQValue.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Multy-Heads Attention Out base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, layer_id, OpenCL, iWindowKey * iHeads * iUnits, optimization, iBatch) || !cMHSelfAttentionOut.Add(base)) class="kw">return false; layer_id++;
交叉注意力层的卷积装配细节
在自注意力输出之后,网络需要接入参考序列做交叉注意力。这段代码用一连串 CNeuronConvOCL 卷积层把 Query、Key、Value 三支通路铺开,每一支的初始化都依赖 iWindow、iWindowKey、iHeads 与 iUnits 的乘积累加,任何一步 Init 返回 false 就直接中断整层构建。 具体看,Query / RefKey / RefValue 三者卷积的输入宽度都是 iWindowKey*iHeads、核宽 iWindow、输出 iUnits,说明参考分支和主序列分支在特征维度上对齐后才进多头融合。随后 CNeuronBaseOCL 以 iWindowKey*iHeads*iUnits 作展平维度接住多头输出,再经 Cross-Attention Out 卷积回 iWindow 时间步。 最后残差支路用 base 层以 iWindow*iUnits 维度直连,Feed Forward 则把卷积核宽拉到 4*iWindow 做非线性扩张,并显式设 LReLU 激活。外汇与贵金属行情高频跳变,这类结构在 MT5 中跑实盘前务必用小样本离线验证梯度连通性,GPU 显存占用可能随 iHeads 增大而陡增。
class=class="str">"cmt">//--- Self-Attention Out conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, iWindowKey * iHeads, iWindowKey * iHeads, iWindow, iUnits, class="num">1, optimization, iBatch) || !cSelfAttentionOut.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Reference Cross-Attention class=class="str">"cmt">//--- Query conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0,layer_id, OpenCL, iWindow, iWindow, iWindowKey*iHeads, iUnits, class="num">1, optimization, iBatch) || !cQuery.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Key conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0,layer_id, OpenCL, iWindow, iWindow, iWindowKey*iHeads, iUnits, class="num">1, optimization, iBatch) || !cRefKey.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Value conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0,layer_id, OpenCL, iWindow, iWindow, iWindowKey*iHeads, iUnits, class="num">1, optimization, iBatch) || !cRefValue.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Multy-Heads Attention Out base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, layer_id, OpenCL, iWindowKey * iHeads * iUnits, optimization, iBatch) || !cMHRefAttentionOut.Add(base)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Cross-Attention Out conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0,layer_id, OpenCL, iWindowKey*iHeads, iWindowKey*iHeads, iWindow, iUnits, class="num">1, optimization, iBatch) || !cRefAttentionOut.Add(conv)) class="kw">return false; layer_id++; if(!conv.SetGradient(((CNeuronBaseOCL*)cSelfAttentionOut[cSelfAttentionOut.Total() - class="num">1]).getGradient(), true)) class="kw">return false; class=class="str">"cmt">//--- Residual base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, layer_id, OpenCL, iWindow * iUnits, optimization, iBatch)) class="kw">return false; if(!cResidual.Add(base)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Feed Forward conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, iWindow, iWindow, class="num">4*iWindow, iUnits, class="num">1, optimization, iBatch)) class="kw">return false; conv.SetActivationFunction(LReLU);
◍ 残差分支与位置卷积的初始化收口
这段初始化收尾把卷积层、残差基元和位置卷积依次挂进对应容器,layer_id 每加一层就递增,任何一步 Init 或 Add 失败直接返回 false,保证 GRES 网络结构在 MT5 端构建时不出现半残状态。 注意位置卷积 cQPosition 里显式调了 conv.SetActivationFunction(SIGMOID),把输出压到 0~1 区间,这跟前面残差分支用的线性基元不一样,做贵金属行情特征提取时 sigmoid 出口更倾向给出概率化的相对位置信号。 feedForward 里写死了 cQuery 循环 5 次、cSuperPoints 按 Total() 动态跑,说明查询子网络层数固定为 5,而超级点层数由外部配置决定。外汇和贵金属杠杆高、滑点随机,这类结构若直接接实盘信号,回测与直播表现可能偏差明显,上机前务必用历史 tick 验证梯度连通性。 最后 SetGradient(base.getGradient()) 把残差末层梯度回灌给本类,OpenCL 上下文靠 SetOpenCL 统一下发,整个 init 返回 true 才代表这张图能进前向。
if(!cFeedForward.Add(conv)) class="kw">return false; layer_id++; conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, class="num">4 * iWindow, class="num">4 * iWindow, iWindow, iUnits, class="num">1, optimization, iBatch)) class="kw">return false; if(!cFeedForward.Add(conv)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Residual base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, layer_id, OpenCL, iWindow * iUnits, optimization, iBatch)) class="kw">return false; if(!base.SetGradient(conv.getGradient())) class="kw">return false; if(!cResidual.Add(base)) class="kw">return false; layer_id++; class=class="str">"cmt">//--- Delta position conv = new CNeuronConvOCL(); if(!conv || !conv.Init(class="num">0, layer_id, OpenCL, iWindow, iWindow, iWindow, iUnits, class="num">1, optimization, iBatch)) class="kw">return false; conv.SetActivationFunction(SIGMOID); if(!cQPosition.Add(conv)) class="kw">return false; layer_id++; base = new CNeuronBaseOCL(); if(!base || !base.Init(class="num">0, layer_id, OpenCL, conv.Neurons(), optimization, iBatch)) class="kw">return false; if(!base.SetGradient(conv.getGradient())) class="kw">return false; if(!cQPosition.Add(base)) class="kw">return false; layer_id++; } base = cResidual[iLayers * class="num">3 - class="num">1]; if(!SetGradient(base.getGradient())) class="kw">return false; class=class="str">"cmt">//--- SetOpenCL(OpenCL); class=class="str">"cmt">//--- class="kw">return true; } class="type">bool CNeuronGRES::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) { class=class="str">"cmt">//--- Superpoints CNeuronBaseOCL *superpoints = NeuronOCL; class="type">int total_sp = cSuperPoints.Total(); for(class="type">int i = class="num">0; i < total_sp; i++) { if(!cSuperPoints[i] || !((CNeuronBaseOCL*)cSuperPoints[i]).FeedForward(superpoints)) class="kw">return false; superpoints = cSuperPoints[i]; } class=class="str">"cmt">//--- Query CNeuronBaseOCL *query = NeuronOCL; for(class="type">int i = class="num">0; i < class="num">5; i++) { if(!cQuery[i] || !((CNeuronBaseOCL*)cQuery[i]).FeedForward(query)) class="kw">return false; query = cQuery[i]; } class=class="str">"cmt">//--- Reference CNeuronBaseOCL *reference = cReference[class="num">0]; if(!SecondInput) class="kw">return false;