diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/generate_attention_fixtures.py b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/generate_attention_fixtures.py new file mode 100644 index 0000000000..62749b7e9e --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/generate_attention_fixtures.py @@ -0,0 +1,480 @@ +#!/usr/bin/env python3 +"""Generate the attention / MoE / KV-cache assignment fixtures. + + python3 tests/fixtures/generate_attention_fixtures.py + +These exist for one assertion: when this EP is loaded it must take every node +it supports, and ORT's CPU EP must be left with nothing. `plugin_ort_e2e.rs` +runs each fixture twice -- once reading ORT's node-to-EP assignment back, and +once with `session.disable_cpu_ep_fallback=1` so an unclaimed node fails +session creation outright. + +The suite that already existed covered activations and normalisation only, so +the guarantee was unproven for exactly the operators this EP is built for. +Every graph here is single-node where the operator allows it, because a +multi-node graph cannot distinguish "the node was claimed" from "the node was +swallowed by a partition around it". + +Shapes are decode-sized on purpose: that is the range the assignment argument +used to be made about, so it is the range the fixtures have to cover. +""" +import os + +import onnx +from onnx import TensorProto, helper +from google.protobuf import text_format + +FIXTURES_DIR = os.path.dirname(os.path.abspath(__file__)) + +MS = "com.microsoft" + + +def save(model, name, check=True): + path = os.path.join(FIXTURES_DIR, name, "model.onnx.textproto") + os.makedirs(os.path.dirname(path), exist_ok=True) + if check: + onnx.checker.check_model(model) + with open(path, "w") as f: + f.write(text_format.MessageToString(model)) + print(f" wrote {path} ({os.path.getsize(path)} bytes)") + + +def vi(name, dims, elem=TensorProto.FLOAT): + return helper.make_tensor_value_info(name, elem, dims) + + +def finish(nodes, name, inputs, outputs, opset=17, ms=False, inits=None): + graph = helper.make_graph(nodes, name, inputs, outputs, initializer=inits or []) + imports = [helper.make_opsetid("", opset)] + if ms: + imports.append(helper.make_opsetid(MS, 1)) + model = helper.make_model(graph, opset_imports=imports) + model.ir_version = 10 + return model + + +# ── Softmax ────────────────────────────────────────────────────────────────── +# The attention softmax shape: [batch=1, heads=32, q=1, kv=1024], reduced over +# the last axis. Kept single-node so the claim cannot be inherited from a +# neighbour. +def gen_softmax_assignment_f32(): + X = vi("X", [1, 32, 1, 1024]) + Z = vi("Z", [1, 32, 1, 1024]) + node = helper.make_node("Softmax", ["X"], ["Z"], axis=-1) + save(finish([node], "softmax_assignment_f32", [X], [Z], opset=13), + "softmax_assignment_f32") + + +# ── Transpose ──────────────────────────────────────────────────────────────── +# The QK layout permutation [B,S,H,D] -> [B,H,S,D], which is the one that +# actually appears between a projection and a QK GEMM. +def gen_transpose_assignment_f32(): + X = vi("X", [1, 128, 32, 128]) + Z = vi("Z", [1, 32, 128, 128]) + node = helper.make_node("Transpose", ["X"], ["Z"], perm=[0, 2, 1, 3]) + save(finish([node], "transpose_assignment_f32", [X], [Z], opset=13), + "transpose_assignment_f32") + + +# ── Concat (KV cache append) ───────────────────────────────────────────────── +# past [B, H, P, D] ++ new [B, H, 1, D] along the sequence axis: the decode-step +# KV cache growth, which is where the Concat cost in this EP actually lives. +def gen_kv_concat_assignment_f32(): + past = vi("past", [1, 8, 1023, 128]) + cur = vi("cur", [1, 8, 1, 128]) + out = vi("present", [1, 8, 1024, 128]) + node = helper.make_node("Concat", ["past", "cur"], ["present"], axis=2) + save(finish([node], "kv_concat_assignment_f32", [past, cur], [out], opset=13), + "kv_concat_assignment_f32") + + +# ── ScatterND (in-place KV cache update) ───────────────────────────────────── +# The other KV-cache idiom: write the new token into a preallocated cache +# instead of growing it. Output shape == data shape, which is the rule that was +# missing from the plugin's shape table. +def gen_kv_scatternd_assignment_f32(): + data = vi("cache", [1, 8, 1024, 128]) + indices = vi("indices", [1, 3], TensorProto.INT64) + updates = vi("updates", [1, 128]) + out = vi("updated", [1, 8, 1024, 128]) + node = helper.make_node("ScatterND", ["cache", "indices", "updates"], ["updated"]) + save(finish([node], "kv_scatternd_assignment_f32", [data, indices, updates], [out], + opset=16), + "kv_scatternd_assignment_f32") + + +# ── RotaryEmbedding (float32) ──────────────────────────────────────────────── +# The op #1078 deferred to ORT on a 12/12 losing grid. That deferral is +# withdrawn, so this fixture is the standing falsifier: if float32 RoPE is ever +# handed back to ORT's CPU EP, both assignment tests fail. +# +# com.microsoft::RotaryEmbedding takes (input, position_ids, cos_cache, +# sin_cache) with input [B, S, hidden] and caches [max_seq, head_size / 2]. +def gen_rotary_assignment_f32(): + B, S, H, D = 1, 1, 32, 128 + x = vi("input", [B, S, H * D]) + pos = vi("position_ids", [B, S], TensorProto.INT64) + cos = vi("cos_cache", [2048, D // 2]) + sin = vi("sin_cache", [2048, D // 2]) + out = vi("output", [B, S, H * D]) + node = helper.make_node( + "RotaryEmbedding", + ["input", "position_ids", "cos_cache", "sin_cache"], + ["output"], + domain=MS, + num_heads=H, + ) + save(finish([node], "rotary_assignment_f32", [x, pos, cos, sin], [out], ms=True), + "rotary_assignment_f32", check=False) + + +# ── MultiHeadAttention ─────────────────────────────────────────────────────── +# Decode-shaped SDPA, no KV cache inputs, so the graph stays single-node. +def gen_mha_assignment_f32(): + B, S, H, D = 1, 1, 32, 128 + hidden = H * D + q = vi("query", [B, S, hidden]) + k = vi("key", [B, S, hidden]) + v = vi("value", [B, S, hidden]) + out = vi("output", [B, S, hidden]) + node = helper.make_node( + "MultiHeadAttention", ["query", "key", "value"], ["output"], + domain=MS, num_heads=H, + ) + save(finish([node], "mha_assignment_f32", [q, k, v], [out], ms=True), + "mha_assignment_f32", check=False) + + +# ── GroupQueryAttention ────────────────────────────────────────────────────── +# GQA with 32 query heads over 8 KV heads -- the llama3-shaped decode step. +# Inputs: q, k, v, past_key, past_value, seqlens_k, total_sequence_length, +# cos_cache, sin_cache. Outputs: output, present_key, present_value. +def gen_gqa_assignment_f32(): + B, S, H, KV, D, P = 1, 1, 32, 8, 128, 1023 + q = vi("query", [B, S, H * D]) + k = vi("key", [B, S, KV * D]) + v = vi("value", [B, S, KV * D]) + pk = vi("past_key", [B, KV, P, D]) + pv = vi("past_value", [B, KV, P, D]) + seqlens = vi("seqlens_k", [B], TensorProto.INT32) + total = vi("total_sequence_length", [1], TensorProto.INT32) + out = vi("output", [B, S, H * D]) + prk = vi("present_key", [B, KV, P + S, D]) + prv = vi("present_value", [B, KV, P + S, D]) + node = helper.make_node( + "GroupQueryAttention", + ["query", "key", "value", "past_key", "past_value", "seqlens_k", + "total_sequence_length"], + ["output", "present_key", "present_value"], + domain=MS, num_heads=H, kv_num_heads=KV, + ) + save(finish([node], "gqa_assignment_f32", + [q, k, v, pk, pv, seqlens, total], [out, prk, prv], ms=True), + "gqa_assignment_f32", check=False) + + +# ── com.microsoft::Attention (packed QKV) ──────────────────────────────────── +# The signature the opset-23 ai.onnx::Attention arm deliberately does not +# cover: input is the *unprojected* activation and the fused weight carries +# q|k|v, so the output width is v_hidden rather than the input's last dim. +# That is the rule this fixture pins. +def gen_msft_attention_assignment_f32(): + B, S, H, D = 1, 8, 4, 16 + hidden = H * D + x = vi("input", [B, S, hidden]) + w = vi("weights", [hidden, 3 * hidden]) + bias = vi("bias", [3 * hidden]) + out = vi("output", [B, S, hidden]) + node = helper.make_node( + "Attention", ["input", "weights", "bias"], ["output"], + domain=MS, num_heads=H, + ) + save(finish([node], "msft_attention_assignment_f32", [x, w, bias], [out], ms=True), + "msft_attention_assignment_f32", check=False) + + +# ── MoE ────────────────────────────────────────────────────────────────────── +# Mixtral-shaped routing at a small hidden size: 8 experts, top-2, SwiGLU-free +# (gelu) so the fixture stays a single node. Output shape == input shape, the +# rule that was missing from the plugin's shape table. +def gen_moe_assignment_f32(): + rows, hidden, experts, inter = 4, 32, 8, 64 + x = vi("input", [rows, hidden]) + probs = vi("router_probs", [rows, experts]) + w1 = vi("fc1_experts_weights", [experts, inter, hidden]) + b1 = vi("fc1_experts_bias", [experts, inter]) + w2 = vi("fc2_experts_weights", [experts, hidden, inter]) + b2 = vi("fc2_experts_bias", [experts, hidden]) + out = vi("output", [rows, hidden]) + node = helper.make_node( + "MoE", + ["input", "router_probs", "fc1_experts_weights", "fc1_experts_bias", + "fc2_experts_weights", "fc2_experts_bias"], + ["output"], + domain=MS, k=2, activation_type="gelu", + ) + save(finish([node], "moe_assignment_f32", + [x, probs, w1, b1, w2, b2], [out], ms=True), + "moe_assignment_f32", check=False) + + +def main(): + print("Generating attention / MoE / KV assignment fixtures …") + gen_softmax_assignment_f32() + gen_transpose_assignment_f32() + gen_kv_concat_assignment_f32() + gen_kv_scatternd_assignment_f32() + gen_rotary_assignment_f32() + gen_mha_assignment_f32() + gen_gqa_assignment_f32() + gen_gqa_rotary_position_ids_assignment_f32() + gen_msft_attention_assignment_f32() + gen_moe_assignment_f32() + gen_moe_assignment_f16() + gen_qmoe_assignment_f32() + gen_qmoe_columnwise_f32() + gen_moe_sparse_mixer_f32() + gen_gqa_smooth_softmax_f32() + gen_packed_mha_assignment_f32() + gen_trilu_assignment_f32() + gen_scatter_elements_assignment_f32() + print("Done.") + + +# ── MoE, float16 ───────────────────────────────────────────────────────────── +# Review falsifier: production mixtures are exported in half precision, and +# `moe.rs` widens f16/bf16 to f32 and narrows on the way out -- but the op +# advertised f32 alone, so every realistic MoE node was declined while the f32 +# fixture passed. One dtype's worth of coverage is not coverage. +def gen_moe_assignment_f16(): + rows, hidden, experts, inter = 4, 32, 8, 64 + x = vi("input", [rows, hidden], TensorProto.FLOAT16) + probs = vi("router_probs", [rows, experts], TensorProto.FLOAT16) + fc1_w = helper.make_tensor( + "fc1_w16", TensorProto.FLOAT16, [experts, inter, hidden], + b"\x00\x3c" * (experts * inter * hidden), raw=True) + fc2_w = helper.make_tensor( + "fc2_w16", TensorProto.FLOAT16, [experts, hidden, inter], + b"\x00\x3c" * (experts * hidden * inter), raw=True) + out = vi("output", [rows, hidden], TensorProto.FLOAT16) + node = helper.make_node( + "MoE", ["input", "router_probs", "fc1_w16", "", "fc2_w16"], ["output"], + domain=MS, k=2, activation_type="gelu") + save(finish([node], "moe_assignment_f16", [x, probs], [out], ms=True, + inits=[fc1_w, fc2_w]), + "moe_assignment_f16", check=False) + + +# ── GroupQueryAttention with explicit int64 position_ids ───────────────────── +# Review falsifier for the per-slot dtype table: `position_ids` is optional +# input *9*, and leaving it off `GQA_SLOTS` made a `do_rotary` node with +# explicit positions fail the float union and go to ORT -- the same class of +# silent decline the table exists to stop, just one slot further out. +def gen_gqa_rotary_position_ids_assignment_f32(): + B, S, H, KV, D, P = 1, 1, 32, 8, 128, 1023 + q = vi("query", [B, S, H * D]) + k = vi("key", [B, S, KV * D]) + v = vi("value", [B, S, KV * D]) + pk = vi("past_key", [B, KV, P, D]) + pv = vi("past_value", [B, KV, P, D]) + seqlens = vi("seqlens_k", [B], TensorProto.INT32) + total = vi("total_sequence_length", [1], TensorProto.INT32) + cos = vi("cos_cache", [2048, D // 2]) + sin = vi("sin_cache", [2048, D // 2]) + pos = vi("position_ids", [B, S], TensorProto.INT64) + out = vi("output", [B, S, H * D]) + prk = vi("present_key", [B, KV, P + S, D]) + prv = vi("present_value", [B, KV, P + S, D]) + node = helper.make_node( + "GroupQueryAttention", + ["query", "key", "value", "past_key", "past_value", "seqlens_k", + "total_sequence_length", "cos_cache", "sin_cache", "position_ids"], + ["output", "present_key", "present_value"], + domain=MS, num_heads=H, kv_num_heads=KV, do_rotary=1, + ) + save(finish([node], "gqa_rotary_pos_assignment_f32", + [q, k, v, pk, pv, seqlens, total, cos, sin, pos], + [out, prk, prv], ms=True), + "gqa_rotary_pos_assignment_f32", check=False) + + +# ── QMoE ───────────────────────────────────────────────────────────────────── +# ORT does have a CPU kernel for QMoE (review loaded and ran this fixture on +# `CPUExecutionProvider` under 1.27 and 1.28), so declining it meant not running +# an op we implement rather than a load failure. int4-packed experts, so the +# weight last dim is halved and the scales carry the block structure. +def gen_qmoe_assignment_f32(): + # Blocked quantization: our kernel supports block_size >= 16 (a power of + # two), so the scales carry one column per block rather than one per row. + # ORT's schema default of 0 selects the column-wise form, which this kernel + # does not implement -- a real capability gap, not a claim gap, and one ORT + # cannot cover either since it has no CPU QMoE kernel at all. + rows, hidden, experts, inter, block = 4, 32, 4, 64, 32 + x = vi("input", [rows, hidden]) + probs = vi("router_probs", [rows, experts]) + fc1_w = helper.make_tensor( + "fc1_w", TensorProto.UINT8, [experts, inter, hidden // 2], + b"\x11" * (experts * inter * hidden // 2), raw=True) + fc1_s = helper.make_tensor( + "fc1_s", TensorProto.FLOAT, [experts, inter, hidden // block], + [0.01] * (experts * inter * (hidden // block))) + fc2_w = helper.make_tensor( + "fc2_w", TensorProto.UINT8, [experts, hidden, inter // 2], + b"\x11" * (experts * hidden * inter // 2), raw=True) + fc2_s = helper.make_tensor( + "fc2_s", TensorProto.FLOAT, [experts, hidden, inter // block], + [0.01] * (experts * hidden * (inter // block))) + out = vi("output", [rows, hidden]) + node = helper.make_node( + "QMoE", + ["input", "router_probs", "fc1_w", "fc1_s", "", "fc2_w", "fc2_s"], + ["output"], + domain=MS, k=2, activation_type="gelu", expert_weight_bits=4, + block_size=block, + ) + save(finish([node], "qmoe_assignment_f32", [x, probs], [out], ms=True, + inits=[fc1_w, fc1_s, fc2_w, fc2_s]), + "qmoe_assignment_f32", check=False) + + +# ── QMoE, column-wise (the one deliberate capability decline) ──────────────── +# `block_size` absent means one scale per output row, and this kernel +# implements only the blocked form. ORT's CPU kernel *does* run this, so +# claiming it and then failing in the factory would take a working model and +# kill the session -- the decline has to happen at claim time instead. +# `plugin_ort_e2e::column_wise_qmoe_is_declined_at_claim_time_not_failed_late` +# is the regression test; the fix is to implement the column-wise path. +def gen_qmoe_columnwise_f32(): + rows, hidden, experts, inter = 4, 32, 4, 64 + x = vi("input", [rows, hidden]) + probs = vi("router_probs", [rows, experts]) + fc1_w = helper.make_tensor( + "cw_fc1_w", TensorProto.UINT8, [experts, inter, hidden // 2], + b"\x11" * (experts * inter * hidden // 2), raw=True) + fc1_s = helper.make_tensor( + "cw_fc1_s", TensorProto.FLOAT, [experts, inter], + [0.01] * (experts * inter)) + fc2_w = helper.make_tensor( + "cw_fc2_w", TensorProto.UINT8, [experts, hidden, inter // 2], + b"\x11" * (experts * hidden * inter // 2), raw=True) + fc2_s = helper.make_tensor( + "cw_fc2_s", TensorProto.FLOAT, [experts, hidden], + [0.01] * (experts * hidden)) + out = vi("output", [rows, hidden]) + node = helper.make_node( + "QMoE", + ["input", "router_probs", "cw_fc1_w", "cw_fc1_s", "", "cw_fc2_w", + "cw_fc2_s"], + ["output"], + domain=MS, k=2, activation_type="gelu", expert_weight_bits=4) + save(finish([node], "qmoe_columnwise_f32", [x, probs], [out], ms=True, + inits=[fc1_w, fc1_s, fc2_w, fc2_s]), + "qmoe_columnwise_f32", check=False) + + +# ── MoE, use_sparse_mixer=1 ────────────────────────────────────────────────── +# Second review falsifier, same class as the column-wise QMoE one above. The +# sparse-mixer router (Phi-3.5-MoE / GRIN-MoE) is rejected by +# `MoeAttributes::from_node`, which runs in the kernel factory -- after ORT has +# already compiled the node onto us, where no fallback recovers. ORT's own CPU +# MoE kernel runs it, so the decline has to happen at claim time. +# `plugin_ort_e2e::factory_only_capability_limits_are_declined_at_claim_time` +# is the regression test; the fix is to implement the sparse mixer. +def gen_moe_sparse_mixer_f32(): + rows, hidden, experts, inter = 4, 32, 8, 64 + x = vi("input", [rows, hidden]) + probs = vi("router_probs", [rows, experts]) + w1 = vi("fc1_experts_weights", [experts, inter, hidden]) + w2 = vi("fc2_experts_weights", [experts, hidden, inter]) + out = vi("output", [rows, hidden]) + node = helper.make_node( + "MoE", + ["input", "router_probs", "fc1_experts_weights", "", + "fc2_experts_weights"], + ["output"], + domain=MS, k=2, activation_type="gelu", use_sparse_mixer=1) + save(finish([node], "moe_sparse_mixer_f32", [x, probs, w1, w2], [out], ms=True), + "moe_sparse_mixer_f32", check=False) + + +# ── GroupQueryAttention, smooth_softmax=1 ──────────────────────────────────── +# Same class again. `smooth_softmax = 1` (Gemma-style attention sink) is +# rejected in the GQA factory; ORT's CPU kernel implements it +# (`use_smooth_softmax_ = ... == 1`), so claiming and then failing turns a model +# that runs today into one that will not load. +def gen_gqa_smooth_softmax_f32(): + B, S, H, KV, D, P = 1, 1, 32, 8, 128, 1023 + q = vi("query", [B, S, H * D]) + k = vi("key", [B, S, KV * D]) + v = vi("value", [B, S, KV * D]) + pk = vi("past_key", [B, KV, P, D]) + pv = vi("past_value", [B, KV, P, D]) + seqlens = vi("seqlens_k", [B], TensorProto.INT32) + total = vi("total_sequence_length", [1], TensorProto.INT32) + out = vi("output", [B, S, H * D]) + prk = vi("present_key", [B, KV, P + S, D]) + prv = vi("present_value", [B, KV, P + S, D]) + node = helper.make_node( + "GroupQueryAttention", + ["query", "key", "value", "past_key", "past_value", "seqlens_k", + "total_sequence_length"], + ["output", "present_key", "present_value"], + domain=MS, num_heads=H, kv_num_heads=KV, smooth_softmax=1, + ) + save(finish([node], "gqa_smooth_softmax_f32", + [q, k, v, pk, pv, seqlens, total], [out, prk, prv], ms=True), + "gqa_smooth_softmax_f32", check=False) + + +# ── PackedMultiHeadAttention ───────────────────────────────────────────────── +# Rank-2 token-packed SDPA: output is [token_count, v_hidden], which is why it +# cannot use the SameAsInput(0) rule the other rescued ops share. +def gen_packed_mha_assignment_f32(): + tokens, H, D, B = 8, 4, 16, 1 + hidden = H * D + q = vi("query", [tokens, hidden]) + k = vi("key", [tokens, hidden]) + v = vi("value", [tokens, hidden]) + token_offset = vi("token_offset", [B, tokens], TensorProto.INT32) + cumseq = vi("cumulative_sequence_length", [B + 1], TensorProto.INT32) + out = vi("output", [tokens, hidden]) + node = helper.make_node( + "PackedMultiHeadAttention", + ["query", "key", "value", "", "token_offset", + "cumulative_sequence_length"], + ["output"], domain=MS, num_heads=H, + ) + save(finish([node], "packed_mha_assignment_f32", + [q, k, v, token_offset, cumseq], [out], ms=True), + "packed_mha_assignment_f32", check=False) + + +# ── Trilu ──────────────────────────────────────────────────────────────────── +# The causal-mask builder. Shape-preserving, so the rule is SameAsInput(0). +def gen_trilu_assignment_f32(): + x = vi("input", [1, 1, 128, 128]) + out = vi("output", [1, 1, 128, 128]) + k = helper.make_tensor("k", TensorProto.INT64, [], [0]) + node = helper.make_node("Trilu", ["input", "k"], ["output"], upper=1) + save(finish([node], "trilu_assignment_f32", [x], [out], inits=[k]), + "trilu_assignment_f32") + + +# ── ScatterElements ────────────────────────────────────────────────────────── +# The other KV-cache write form, alongside ScatterND. +def gen_scatter_elements_assignment_f32(): + B, KV, T, D = 1, 8, 1024, 128 + data = vi("data", [B, KV, T, D]) + updates = vi("updates", [B, KV, 1, D]) + indices = helper.make_tensor( + "indices", TensorProto.INT64, [B, KV, 1, D], [0] * (B * KV * D)) + out = vi("output", [B, KV, T, D]) + node = helper.make_node( + "ScatterElements", ["data", "indices", "updates"], ["output"], axis=2) + save(finish([node], "scatter_elements_assignment_f32", + [data, updates], [out], inits=[indices]), + "scatter_elements_assignment_f32") + + +if __name__ == "__main__": + main() diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..2a9c3c3e73 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_assignment_f32/model.onnx.textproto @@ -0,0 +1,226 @@ +ir_version: 10 +graph { + node { + input: "query" + input: "key" + input: "value" + input: "past_key" + input: "past_value" + input: "seqlens_k" + input: "total_sequence_length" + output: "output" + output: "present_key" + output: "present_value" + op_type: "GroupQueryAttention" + attribute { + name: "kv_num_heads" + i: 8 + type: INT + } + attribute { + name: "num_heads" + i: 32 + type: INT + } + domain: "com.microsoft" + } + name: "gqa_assignment_f32" + input { + name: "query" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + input { + name: "key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 1024 + } + } + } + } + } + input { + name: "value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 1024 + } + } + } + } + } + input { + name: "past_key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1023 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "past_value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1023 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "seqlens_k" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 1 + } + } + } + } + } + input { + name: "total_sequence_length" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 1 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + output { + name: "present_key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } + output { + name: "present_value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_rotary_pos_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_rotary_pos_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..74135026fd --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_rotary_pos_assignment_f32/model.onnx.textproto @@ -0,0 +1,282 @@ +ir_version: 10 +graph { + node { + input: "query" + input: "key" + input: "value" + input: "past_key" + input: "past_value" + input: "seqlens_k" + input: "total_sequence_length" + input: "cos_cache" + input: "sin_cache" + input: "position_ids" + output: "output" + output: "present_key" + output: "present_value" + op_type: "GroupQueryAttention" + attribute { + name: "do_rotary" + i: 1 + type: INT + } + attribute { + name: "kv_num_heads" + i: 8 + type: INT + } + attribute { + name: "num_heads" + i: 32 + type: INT + } + domain: "com.microsoft" + } + name: "gqa_rotary_pos_assignment_f32" + input { + name: "query" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + input { + name: "key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 1024 + } + } + } + } + } + input { + name: "value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 1024 + } + } + } + } + } + input { + name: "past_key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1023 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "past_value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1023 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "seqlens_k" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 1 + } + } + } + } + } + input { + name: "total_sequence_length" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 1 + } + } + } + } + } + input { + name: "cos_cache" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2048 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "sin_cache" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2048 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "position_ids" + type { + tensor_type { + elem_type: 7 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + output { + name: "present_key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } + output { + name: "present_value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_smooth_softmax_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_smooth_softmax_f32/model.onnx.textproto new file mode 100644 index 0000000000..7d927cce18 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/gqa_smooth_softmax_f32/model.onnx.textproto @@ -0,0 +1,231 @@ +ir_version: 10 +graph { + node { + input: "query" + input: "key" + input: "value" + input: "past_key" + input: "past_value" + input: "seqlens_k" + input: "total_sequence_length" + output: "output" + output: "present_key" + output: "present_value" + op_type: "GroupQueryAttention" + attribute { + name: "kv_num_heads" + i: 8 + type: INT + } + attribute { + name: "num_heads" + i: 32 + type: INT + } + attribute { + name: "smooth_softmax" + i: 1 + type: INT + } + domain: "com.microsoft" + } + name: "gqa_smooth_softmax_f32" + input { + name: "query" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + input { + name: "key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 1024 + } + } + } + } + } + input { + name: "value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 1024 + } + } + } + } + } + input { + name: "past_key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1023 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "past_value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1023 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "seqlens_k" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 1 + } + } + } + } + } + input { + name: "total_sequence_length" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 1 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + output { + name: "present_key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } + output { + name: "present_value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/kv_concat_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/kv_concat_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..67a86c227e --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/kv_concat_assignment_f32/model.onnx.textproto @@ -0,0 +1,85 @@ +ir_version: 10 +graph { + node { + input: "past" + input: "cur" + output: "present" + op_type: "Concat" + attribute { + name: "axis" + i: 2 + type: INT + } + } + name: "kv_concat_assignment_f32" + input { + name: "past" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1023 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "cur" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1 + } + dim { + dim_value: 128 + } + } + } + } + } + output { + name: "present" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } +} +opset_import { + domain: "" + version: 13 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/kv_scatternd_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/kv_scatternd_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..66870a6c05 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/kv_scatternd_assignment_f32/model.onnx.textproto @@ -0,0 +1,91 @@ +ir_version: 10 +graph { + node { + input: "cache" + input: "indices" + input: "updates" + output: "updated" + op_type: "ScatterND" + } + name: "kv_scatternd_assignment_f32" + input { + name: "cache" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "indices" + type { + tensor_type { + elem_type: 7 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 3 + } + } + } + } + } + input { + name: "updates" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 128 + } + } + } + } + } + output { + name: "updated" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } +} +opset_import { + domain: "" + version: 16 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/mha_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/mha_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..afeed0e8bd --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/mha_assignment_f32/model.onnx.textproto @@ -0,0 +1,101 @@ +ir_version: 10 +graph { + node { + input: "query" + input: "key" + input: "value" + output: "output" + op_type: "MultiHeadAttention" + attribute { + name: "num_heads" + i: 32 + type: INT + } + domain: "com.microsoft" + } + name: "mha_assignment_f32" + input { + name: "query" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + input { + name: "key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + input { + name: "value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_assignment_f16/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_assignment_f16/model.onnx.textproto new file mode 100644 index 0000000000..0533ed8ac5 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_assignment_f16/model.onnx.textproto @@ -0,0 +1,96 @@ +ir_version: 10 +graph { + node { + input: "input" + input: "router_probs" + input: "fc1_w16" + input: "" + input: "fc2_w16" + output: "output" + op_type: "MoE" + attribute { + name: "activation_type" + s: "gelu" + type: STRING + } + attribute { + name: "k" + i: 2 + type: INT + } + domain: "com.microsoft" + } + name: "moe_assignment_f16" + initializer { + dims: 8 + dims: 64 + dims: 32 + data_type: 10 + name: "fc1_w16" + raw_data: "\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<" + } + initializer { + dims: 8 + dims: 32 + dims: 64 + data_type: 10 + name: "fc2_w16" + raw_data: "\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<\000<" + } + input { + name: "input" + type { + tensor_type { + elem_type: 10 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } + input { + name: "router_probs" + type { + tensor_type { + elem_type: 10 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 8 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 10 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..7050a5d2cd --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_assignment_f32/model.onnx.textproto @@ -0,0 +1,151 @@ +ir_version: 10 +graph { + node { + input: "input" + input: "router_probs" + input: "fc1_experts_weights" + input: "fc1_experts_bias" + input: "fc2_experts_weights" + input: "fc2_experts_bias" + output: "output" + op_type: "MoE" + attribute { + name: "activation_type" + s: "gelu" + type: STRING + } + attribute { + name: "k" + i: 2 + type: INT + } + domain: "com.microsoft" + } + name: "moe_assignment_f32" + input { + name: "input" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } + input { + name: "router_probs" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 8 + } + } + } + } + } + input { + name: "fc1_experts_weights" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + dim { + dim_value: 32 + } + } + } + } + } + input { + name: "fc1_experts_bias" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "fc2_experts_weights" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 32 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "fc2_experts_bias" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 32 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_sparse_mixer_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_sparse_mixer_f32/model.onnx.textproto new file mode 100644 index 0000000000..d4264b7fdb --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/moe_sparse_mixer_f32/model.onnx.textproto @@ -0,0 +1,123 @@ +ir_version: 10 +graph { + node { + input: "input" + input: "router_probs" + input: "fc1_experts_weights" + input: "" + input: "fc2_experts_weights" + output: "output" + op_type: "MoE" + attribute { + name: "activation_type" + s: "gelu" + type: STRING + } + attribute { + name: "k" + i: 2 + type: INT + } + attribute { + name: "use_sparse_mixer" + i: 1 + type: INT + } + domain: "com.microsoft" + } + name: "moe_sparse_mixer_f32" + input { + name: "input" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } + input { + name: "router_probs" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 8 + } + } + } + } + } + input { + name: "fc1_experts_weights" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + dim { + dim_value: 32 + } + } + } + } + } + input { + name: "fc2_experts_weights" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 32 + } + dim { + dim_value: 64 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/msft_attention_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/msft_attention_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..96577a8691 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/msft_attention_assignment_f32/model.onnx.textproto @@ -0,0 +1,92 @@ +ir_version: 10 +graph { + node { + input: "input" + input: "weights" + input: "bias" + output: "output" + op_type: "Attention" + attribute { + name: "num_heads" + i: 4 + type: INT + } + domain: "com.microsoft" + } + name: "msft_attention_assignment_f32" + input { + name: "input" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "weights" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 64 + } + dim { + dim_value: 192 + } + } + } + } + } + input { + name: "bias" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 192 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/packed_mha_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/packed_mha_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..ac102edec9 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/packed_mha_assignment_f32/model.onnx.textproto @@ -0,0 +1,121 @@ +ir_version: 10 +graph { + node { + input: "query" + input: "key" + input: "value" + input: "" + input: "token_offset" + input: "cumulative_sequence_length" + output: "output" + op_type: "PackedMultiHeadAttention" + attribute { + name: "num_heads" + i: 4 + type: INT + } + domain: "com.microsoft" + } + name: "packed_mha_assignment_f32" + input { + name: "query" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "key" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "value" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "token_offset" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + } + } + } + } + input { + name: "cumulative_sequence_length" + type { + tensor_type { + elem_type: 6 + shape { + dim { + dim_value: 2 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 8 + } + dim { + dim_value: 64 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/qmoe_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/qmoe_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..f5704053f6 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/qmoe_assignment_f32/model.onnx.textproto @@ -0,0 +1,634 @@ +ir_version: 10 +graph { + node { + input: "input" + input: "router_probs" + input: "fc1_w" + input: "fc1_s" + input: "" + input: "fc2_w" + input: "fc2_s" + output: "output" + op_type: "QMoE" + attribute { + name: "activation_type" + s: "gelu" + type: STRING + } + attribute { + name: "block_size" + i: 32 + type: INT + } + attribute { + name: "expert_weight_bits" + i: 4 + type: INT + } + attribute { + name: "k" + i: 2 + type: INT + } + domain: "com.microsoft" + } + name: "qmoe_assignment_f32" + initializer { + dims: 4 + dims: 64 + dims: 16 + data_type: 2 + name: "fc1_w" + raw_data: "\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021" + } + initializer { + dims: 4 + dims: 64 + dims: 1 + data_type: 1 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + name: "fc1_s" + } + initializer { + dims: 4 + dims: 32 + dims: 32 + data_type: 2 + name: "fc2_w" + raw_data: "\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021" + } + initializer { + dims: 4 + dims: 32 + dims: 2 + data_type: 1 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + name: "fc2_s" + } + input { + name: "input" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } + input { + name: "router_probs" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 4 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/qmoe_columnwise_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/qmoe_columnwise_f32/model.onnx.textproto new file mode 100644 index 0000000000..3d39ebf1ef --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/qmoe_columnwise_f32/model.onnx.textproto @@ -0,0 +1,499 @@ +ir_version: 10 +graph { + node { + input: "input" + input: "router_probs" + input: "cw_fc1_w" + input: "cw_fc1_s" + input: "" + input: "cw_fc2_w" + input: "cw_fc2_s" + output: "output" + op_type: "QMoE" + attribute { + name: "activation_type" + s: "gelu" + type: STRING + } + attribute { + name: "expert_weight_bits" + i: 4 + type: INT + } + attribute { + name: "k" + i: 2 + type: INT + } + domain: "com.microsoft" + } + name: "qmoe_columnwise_f32" + initializer { + dims: 4 + dims: 64 + dims: 16 + data_type: 2 + name: "cw_fc1_w" + raw_data: "\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021" + } + initializer { + dims: 4 + dims: 64 + data_type: 1 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + name: "cw_fc1_s" + } + initializer { + dims: 4 + dims: 32 + dims: 32 + data_type: 2 + name: "cw_fc2_w" + raw_data: "\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021" + } + initializer { + dims: 4 + dims: 32 + data_type: 1 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + float_data: 0.01 + name: "cw_fc2_s" + } + input { + name: "input" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } + input { + name: "router_probs" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 4 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 4 + } + dim { + dim_value: 32 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/rotary_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/rotary_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..80174c9877 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/rotary_assignment_f32/model.onnx.textproto @@ -0,0 +1,112 @@ +ir_version: 10 +graph { + node { + input: "input" + input: "position_ids" + input: "cos_cache" + input: "sin_cache" + output: "output" + op_type: "RotaryEmbedding" + attribute { + name: "num_heads" + i: 32 + type: INT + } + domain: "com.microsoft" + } + name: "rotary_assignment_f32" + input { + name: "input" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } + input { + name: "position_ids" + type { + tensor_type { + elem_type: 7 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + } + } + } + } + input { + name: "cos_cache" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2048 + } + dim { + dim_value: 64 + } + } + } + } + } + input { + name: "sin_cache" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 2048 + } + dim { + dim_value: 64 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 4096 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} +opset_import { + domain: "com.microsoft" + version: 1 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/scatter_elements_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/scatter_elements_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..ad872e896b --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/scatter_elements_assignment_f32/model.onnx.textproto @@ -0,0 +1,1118 @@ +ir_version: 10 +graph { + node { + input: "data" + input: "indices" + input: "updates" + output: "output" + op_type: "ScatterElements" + attribute { + name: "axis" + i: 2 + type: INT + } + } + name: "scatter_elements_assignment_f32" + initializer { + dims: 1 + dims: 8 + dims: 1 + dims: 128 + data_type: 7 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + int64_data: 0 + name: "indices" + } + input { + name: "data" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } + input { + name: "updates" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1 + } + dim { + dim_value: 128 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 8 + } + dim { + dim_value: 1024 + } + dim { + dim_value: 128 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/softmax_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/softmax_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..e72a104083 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/softmax_assignment_f32/model.onnx.textproto @@ -0,0 +1,62 @@ +ir_version: 10 +graph { + node { + input: "X" + output: "Z" + op_type: "Softmax" + attribute { + name: "axis" + i: -1 + type: INT + } + } + name: "softmax_assignment_f32" + input { + name: "X" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 32 + } + dim { + dim_value: 1 + } + dim { + dim_value: 1024 + } + } + } + } + } + output { + name: "Z" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 32 + } + dim { + dim_value: 1 + } + dim { + dim_value: 1024 + } + } + } + } + } +} +opset_import { + domain: "" + version: 13 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/transpose_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/transpose_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..f5c2aac094 --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/transpose_assignment_f32/model.onnx.textproto @@ -0,0 +1,65 @@ +ir_version: 10 +graph { + node { + input: "X" + output: "Z" + op_type: "Transpose" + attribute { + name: "perm" + ints: 0 + ints: 2 + ints: 1 + ints: 3 + type: INTS + } + } + name: "transpose_assignment_f32" + input { + name: "X" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 128 + } + dim { + dim_value: 32 + } + dim { + dim_value: 128 + } + } + } + } + } + output { + name: "Z" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 32 + } + dim { + dim_value: 128 + } + dim { + dim_value: 128 + } + } + } + } + } +} +opset_import { + domain: "" + version: 13 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/trilu_assignment_f32/model.onnx.textproto b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/trilu_assignment_f32/model.onnx.textproto new file mode 100644 index 0000000000..ab73e1a03b --- /dev/null +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/fixtures/trilu_assignment_f32/model.onnx.textproto @@ -0,0 +1,68 @@ +ir_version: 10 +graph { + node { + input: "input" + input: "k" + output: "output" + op_type: "Trilu" + attribute { + name: "upper" + i: 1 + type: INT + } + } + name: "trilu_assignment_f32" + initializer { + data_type: 7 + int64_data: 0 + name: "k" + } + input { + name: "input" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 128 + } + dim { + dim_value: 128 + } + } + } + } + } + output { + name: "output" + type { + tensor_type { + elem_type: 1 + shape { + dim { + dim_value: 1 + } + dim { + dim_value: 1 + } + dim { + dim_value: 128 + } + dim { + dim_value: 128 + } + } + } + } + } +} +opset_import { + domain: "" + version: 17 +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/plugin_ort_e2e.rs b/crates/onnx-runtime-ep-cpu-plugin/tests/plugin_ort_e2e.rs index 90d3a1912e..0747064c47 100644 --- a/crates/onnx-runtime-ep-cpu-plugin/tests/plugin_ort_e2e.rs +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/plugin_ort_e2e.rs @@ -3846,6 +3846,49 @@ const ASSIGNMENT_FIXTURES: &[(&str, &str)] = &[ // dropped the claim and ORT ran it. Without a fixture whose op sits in that // gap, the sweep below can only prove the ops it already knew about. ("sin_assignment_f32", "Sin"), + // ── Attention, MoE and KV cache ────────────────────────────────────── + // The suite above is activations and normalisation. Those are not what + // this EP is for, so until these rows existed the architectural rule was + // unproven for exactly the operators the rule was written about. + // + // Five of these could not have passed before: `com.microsoft::Attention`, + // `MoE`, `PackedMultiHeadAttention`, `ScatterND` and `Trilu` had no entry + // in the plugin's `ShapeInference` table, so `GetCapability`'s fail-closed + // filter dropped the claim and ORT's CPU EP ran them -- silently, and + // regardless of what `supports_op` said. + ("softmax_assignment_f32", "Softmax"), + ("transpose_assignment_f32", "Transpose"), + ("kv_concat_assignment_f32", "Concat"), + ("kv_scatternd_assignment_f32", "ScatterND"), + // float32 `RotaryEmbedding` was deferred to ORT by #1078 on a 12/12 + // losing grid. That deferral is withdrawn: this row is the falsifier that + // fails if it is ever reintroduced. + ("rotary_assignment_f32", "RotaryEmbedding"), + ("mha_assignment_f32", "MultiHeadAttention"), + ("gqa_assignment_f32", "GroupQueryAttention"), + // `position_ids` is GQA's optional input *9*. Leaving that slot off the + // per-slot dtype table sent a `do_rotary` node with explicit int64 + // positions to ORT even after the `seqlens_k` / `total_sequence_length` + // slots were fixed, so it gets its own row rather than being assumed. + ("gqa_rotary_pos_assignment_f32", "GroupQueryAttention"), + ("msft_attention_assignment_f32", "Attention"), + ("moe_assignment_f32", "MoE"), + // f32 alone was not coverage: production mixtures are exported in half + // precision, and `MoE` advertised f32 only while its kernel widens f16 and + // bf16 to f32 and narrows on the way out. This row is the falsifier. + ("moe_assignment_f16", "MoE"), + // ORT has no CPU kernel for PackedMultiHeadAttention, so for that one the + // shape-table decline bought a *load failure* rather than a slower run; it + // does have CPU kernels for MoE and QMoE, so for those it was simply us not + // running an op we implement. Each was rescued here and each needs its own real + // session to prove it, because the pure-Rust inventory test builds + // synthetic nodes and never opens one -- it cannot see a dtype-filter or + // kernel-factory rejection, which are exactly the layers that were + // silently declining GQA and RoPE. + ("qmoe_assignment_f32", "QMoE"), + ("packed_mha_assignment_f32", "PackedMultiHeadAttention"), + ("trilu_assignment_f32", "Trilu"), + ("scatter_elements_assignment_f32", "ScatterElements"), ]; /// The architectural rule, asserted directly: when this EP is loaded it takes @@ -3866,6 +3909,7 @@ const ASSIGNMENT_FIXTURES: &[(&str, &str)] = &[ fn no_supported_node_is_ever_left_to_the_ort_cpu_ep() { let _lock = lock_ort_ep(); let mut checked = 0usize; + let mut failures: Vec = Vec::new(); for (fixture, op) in ASSIGNMENT_FIXTURES { let reg = format!("cpu_ep_noyield_{fixture}"); @@ -3878,19 +3922,26 @@ fn no_supported_node_is_ever_left_to_the_ort_cpu_ep() { continue; }; - assert!( - ours.iter().any(|claimed| claimed == op), - "{fixture}: '{op}' must run on this EP, but it is not among {ours:?} \ - (ORT was given {theirs:?})" - ); - assert!( - !theirs.iter().any(|left| left == op), - "{fixture}: '{op}' was handed to ORT's CPU EP. This EP does not defer — \ - a range where it is slower is a kernel to optimize, not a node to give away." - ); + // Collected rather than asserted per fixture: a bare `assert!` stops at + // the first decline, so a run reports one op when several are being + // given away and the next iteration only uncovers the next one. The + // whole matrix is the useful output. + if !ours.iter().any(|claimed| claimed == op) || theirs.iter().any(|left| left == op) { + failures.push(format!( + " {fixture}: '{op}' is not on this EP — ours={ours:?}, ORT was given {theirs:?}" + )); + } checked += 1; } + assert!( + failures.is_empty(), + "{} of {checked} fixtures were handed to ORT's CPU EP. This EP does not defer — \ + a range where it is slower is a kernel to optimize, not a node to give away.\n{}", + failures.len(), + failures.join("\n") + ); + if checked != ASSIGNMENT_FIXTURES.len() { eprintln!( "*** SKIPPED: only {checked}/{} fixtures ran — ORT or EP cdylib not found ***", @@ -5889,3 +5940,324 @@ fn plugin_path_ab_vs_plain_ort() { } } } + +/// float32 `com.microsoft::RotaryEmbedding` and `GroupQueryAttention` execute +/// on *this* EP and agree with ORT's own CPU kernels. +/// +/// Assignment alone is not the guarantee the policy asks for: a node can be +/// claimed and then compute the wrong thing, which is strictly worse than the +/// deferral it replaced. These two ops are the ones that were being handed to +/// ORT until the per-slot dtype tables landed, so they are the two that most +/// need their arithmetic checked against the implementation they displaced. +/// +/// The baseline is a second session over the *same* model file with our EP +/// simply not appended, so ORT resolves both nodes to its own contrib CPU +/// kernels. Same bytes in, so any difference is ours. +#[test] +fn rope_and_gqa_execute_on_our_ep_and_match_ort_numerics() { + let _lock = lock_ort_ep(); + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + + for (fixture, op) in [ + ("rotary_assignment_f32", "RotaryEmbedding"), + ("gqa_assignment_f32", "GroupQueryAttention"), + ] { + let model_path = PathBuf::from(manifest_dir) + .join(format!("tests/fixtures/{fixture}/model.onnx.textproto")); + let reg = format!("cpu_ep_num_{fixture}"); + + let Some((_lib, api, env, opts, session)) = + (unsafe { conformance_setup(®, &model_path, true) }) + else { + eprintln!("*** SKIPPED: {fixture} numerics — ORT or EP cdylib not found ***"); + return; + }; + + unsafe { + let info = query_ep_assignment(api, session); + assert!( + info.ops_on_our_ep().contains(&op), + "{fixture}: '{op}' must execute here, not on ORT's CPU EP; got {:?}", + info.assignments + ); + + let (names, mut inputs, output_names) = attention_fixture_inputs(api, fixture); + let ours = run_and_collect_f32(api, session, &names, &inputs, &output_names); + + // Same model, same bytes, but ORT resolves the node itself. + let mut base_opts: *mut ort::OrtSessionOptions = ptr::null_mut(); + let status = ((*api).CreateSessionOptions.unwrap())(&mut base_opts); + check_status(api, status, "CreateSessionOptions(baseline)"); + let mut base_session: *mut ort::OrtSession = ptr::null_mut(); + let status = + ort_session::create_session(api, env, base_opts, &model_path, &mut base_session); + check_status(api, status, "CreateSession(baseline)"); + let theirs = run_and_collect_f32(api, base_session, &names, &inputs, &output_names); + ((*api).ReleaseSession.unwrap())(base_session); + ((*api).ReleaseSessionOptions.unwrap())(base_opts); + + assert_eq!(ours.len(), theirs.len(), "{fixture}: output count differs"); + for (idx, (a, b)) in ours.iter().zip(theirs.iter()).enumerate() { + assert_eq!( + a.len(), + b.len(), + "{fixture}: output {idx} length {} vs ORT's {}", + a.len(), + b.len() + ); + let mut worst = 0.0f32; + let mut worst_at = 0usize; + for (i, (x, y)) in a.iter().zip(b.iter()).enumerate() { + assert!( + x.is_finite(), + "{fixture}: output {idx}[{i}] is {x}, not finite" + ); + let err = (x - y).abs() / y.abs().max(1.0); + if err > worst { + worst = err; + worst_at = i; + } + } + assert!( + worst <= 2e-5, + "{fixture}: output {idx} differs from ORT by {worst} at [{worst_at}] \ + (ours {}, ORT {})", + a[worst_at], + b[worst_at] + ); + eprintln!( + " [{fixture}] output {idx}: {} values, max rel err {worst:.3e}", + a.len() + ); + } + + for v in inputs.drain(..) { + ((*api).ReleaseValue.unwrap())(v); + } + conformance_teardown(api, env, opts, session, ®); + } + } +} + +/// Deterministic inputs for the attention fixtures, in each model's slot order. +/// +/// The tensors are leaked rather than returned by value because ORT borrows the +/// caller's buffers: `CreateTensorWithDataAsOrtValue` does not copy, so the +/// backing storage has to outlive every `Run` that reads it. +unsafe fn attention_fixture_inputs( + api: *const ort::OrtApi, + fixture: &str, +) -> ( + Vec<&'static str>, + Vec<*mut ort::OrtValue>, + Vec<&'static str>, +) { + // A cheap deterministic spread in [-1, 1) that is not periodic in the head + // dimension, so a transposed or mis-strided read cannot look correct. + let fill = |n: usize, seed: u32| -> &'static mut [f32] { + let mut s = seed | 1; + let v: Vec = (0..n) + .map(|_| { + s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223); + ((s >> 8) as f32 / (1u32 << 23) as f32) - 1.0 + }) + .collect(); + Box::leak(v.into_boxed_slice()) + }; + unsafe { + match fixture { + "rotary_assignment_f32" => { + let (b, s, h, d) = (1i64, 1i64, 32i64, 128i64); + let x = make_float_tensor(api, fill((b * s * h * d) as usize, 7), &[b, s, h * d]); + let pos = Box::leak(vec![17i64].into_boxed_slice()); + let pos_bytes = std::slice::from_raw_parts_mut( + pos.as_mut_ptr().cast::(), + std::mem::size_of_val(pos), + ); + let pos_v = make_raw_tensor( + api, + pos_bytes, + &[b, s], + ort::ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64, + ); + let cos = make_float_tensor(api, fill((2048 * d / 2) as usize, 11), &[2048, d / 2]); + let sin = make_float_tensor(api, fill((2048 * d / 2) as usize, 13), &[2048, d / 2]); + ( + vec!["input", "position_ids", "cos_cache", "sin_cache"], + vec![x, pos_v, cos, sin], + vec!["output"], + ) + } + "gqa_assignment_f32" => { + let (b, s, h, kv, d, p) = (1i64, 1i64, 32i64, 8i64, 128i64, 1023i64); + let q = make_float_tensor(api, fill((b * s * h * d) as usize, 3), &[b, s, h * d]); + let k = make_float_tensor(api, fill((b * s * kv * d) as usize, 5), &[b, s, kv * d]); + let v = make_float_tensor(api, fill((b * s * kv * d) as usize, 9), &[b, s, kv * d]); + let pk = + make_float_tensor(api, fill((b * kv * p * d) as usize, 21), &[b, kv, p, d]); + let pv = + make_float_tensor(api, fill((b * kv * p * d) as usize, 23), &[b, kv, p, d]); + // seqlens_k is the *past* length (ORT adds the new token), and + // total_sequence_length must equal max(seqlens_k) + 1. + // Leaked for the same reason as the float buffers: ORT keeps a + // pointer into them for the life of the value, so a temporary + // would dangle by the time `Run` reads it. + let seqlens = + make_int32_tensor(api, Box::leak(vec![p as i32].into_boxed_slice()), &[b]); + let total = make_int32_tensor( + api, + Box::leak(vec![(p + 1) as i32].into_boxed_slice()), + &[1], + ); + ( + vec![ + "query", + "key", + "value", + "past_key", + "past_value", + "seqlens_k", + "total_sequence_length", + ], + vec![q, k, v, pk, pv, seqlens, total], + vec!["output", "present_key", "present_value"], + ) + } + other => panic!("attention_fixture_inputs: no inputs defined for {other}"), + } + } +} + +/// Run `session` over the named inputs and copy every output out as float32. +unsafe fn run_and_collect_f32( + api: *const ort::OrtApi, + session: *mut ort::OrtSession, + input_names: &[&str], + inputs: &[*mut ort::OrtValue], + output_names: &[&str], +) -> Vec> { + unsafe { + let in_c: Vec = input_names + .iter() + .map(|n| std::ffi::CString::new(*n).unwrap()) + .collect(); + let out_c: Vec = output_names + .iter() + .map(|n| std::ffi::CString::new(*n).unwrap()) + .collect(); + let in_ptrs: Vec<*const i8> = in_c.iter().map(|c| c.as_ptr()).collect(); + let out_ptrs: Vec<*const i8> = out_c.iter().map(|c| c.as_ptr()).collect(); + let mut outputs: Vec<*mut ort::OrtValue> = vec![ptr::null_mut(); output_names.len()]; + + let status = ((*api).Run.unwrap())( + session, + ptr::null(), + in_ptrs.as_ptr(), + inputs.as_ptr().cast(), + inputs.len(), + out_ptrs.as_ptr(), + output_names.len(), + outputs.as_mut_ptr(), + ); + check_status(api, status, "Run(attention fixture)"); + + let mut collected = Vec::with_capacity(outputs.len()); + for out in &outputs { + assert!(!out.is_null(), "Run returned a null output"); + let mut info: *mut ort::OrtTensorTypeAndShapeInfo = ptr::null_mut(); + let status = ((*api).GetTensorTypeAndShape.unwrap())(*out, &mut info); + check_status(api, status, "GetTensorTypeAndShape(attention fixture)"); + let mut count = 0usize; + let status = ((*api).GetTensorShapeElementCount.unwrap())(info, &mut count); + check_status(api, status, "GetTensorShapeElementCount(attention fixture)"); + ((*api).ReleaseTensorTypeAndShapeInfo.unwrap())(info); + + let mut data: *mut std::ffi::c_void = ptr::null_mut(); + let status = ((*api).GetTensorMutableData.unwrap())(*out, &mut data); + check_status(api, status, "GetTensorMutableData(attention fixture)"); + collected.push(std::slice::from_raw_parts(data as *const f32, count).to_vec()); + } + for out in outputs { + ((*api).ReleaseValue.unwrap())(out); + } + collected + } +} + +/// Configurations these kernels cannot compute, each declined at *claim* time +/// with the session still loading. +/// +/// Every row is a **capability** answer rather than a performance one, and each +/// mirrors a rejection its kernel factory raises. The mirroring is what matters: +/// +/// * decline at claim time → ORT runs it, model works, we owe a kernel; +/// * claim and fail in the factory → `CreateSession` dies and **no fallback +/// recovers**, because ORT has already compiled the node onto this EP. +/// +/// Making these ops reachable at all is what created the second possibility, so +/// this test pins the first. ORT's CPU EP runs all three configurations below, +/// which is why claiming-then-failing would take a model that works today and +/// make it unloadable. +/// +/// | fixture | limit | where the factory raises it | +/// |---|---|---| +/// | `qmoe_columnwise_f32` | `block_size` absent → column-wise quantization | `qmoe.rs` | +/// | `moe_sparse_mixer_f32` | `use_sparse_mixer=1` (Phi-3.5-MoE / GRIN-MoE) | `moe.rs` | +/// | `gqa_smooth_softmax_f32` | `smooth_softmax=1` (attention sink) | `group_query_attention.rs` | +/// +/// They are deliberately *not* in `ASSIGNMENT_FIXTURES`: they are the documented +/// exceptions, and each should stop being one as soon as its kernel exists. +#[test] +fn factory_only_capability_limits_are_declined_at_claim_time() { + const CASES: &[(&str, &str, &str)] = &[ + ( + "qmoe_columnwise_f32", + "QMoE", + "`qmoe::unsupported_reason` (column-wise quantization)", + ), + ( + "moe_sparse_mixer_f32", + "MoE", + "`moe::unsupported_reason` (use_sparse_mixer=1)", + ), + ( + "gqa_smooth_softmax_f32", + "GroupQueryAttention", + "`group_query_attention::unsupported_reason` (smooth_softmax=1)", + ), + ]; + + let _lock = lock_ort_ep(); + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + for (fixture, op, guard) in CASES { + let model_path = PathBuf::from(manifest_dir) + .join(format!("tests/fixtures/{fixture}/model.onnx.textproto")); + let reg = format!("cpu_ep_claim_time_{fixture}"); + + // Fallback must stay *enabled*: the point is that ORT can still run it. + let Some((_lib, api, env, opts, session)) = + (unsafe { conformance_setup(®, &model_path, false) }) + else { + eprintln!("*** SKIPPED: {fixture} — ORT or EP cdylib not found ***"); + return; + }; + + unsafe { + let info = query_ep_assignment(api, session); + let ours = info.ops_on_our_ep(); + assert!( + !ours.contains(op), + "{fixture}: {op} was claimed, but the kernel factory rejects this \ + configuration, which kills the session outright rather than falling back. \ + Decline it in {guard}, or implement the missing path. Got: {:?}", + info.assignments + ); + eprintln!( + " [{fixture}] declined at claim time, session loaded; ours={ours:?}, \ + others={:?}", + info.ops_not_on_our_ep() + ); + conformance_teardown(api, env, opts, session, ®); + } + } +} diff --git a/crates/onnx-runtime-ep-cpu-plugin/tests/shape_inference_coverage.rs b/crates/onnx-runtime-ep-cpu-plugin/tests/shape_inference_coverage.rs index 2940b9dbe9..9f20cda1b4 100644 --- a/crates/onnx-runtime-ep-cpu-plugin/tests/shape_inference_coverage.rs +++ b/crates/onnx-runtime-ep-cpu-plugin/tests/shape_inference_coverage.rs @@ -79,9 +79,6 @@ const DECLINED: &[(&str, &str)] = &[ ("", "DequantizeLinear"), ("", "EyeLike"), ("", "QuantizeLinear"), - ("", "ScatterElements"), - ("", "ScatterND"), - ("", "Trilu"), // Pooling and CNN geometry: inferrable from `kernel_shape`, // `strides`, `pads`, `dilations` and `ceil_mode`, exactly as // `build_conv` already does for `Conv`. @@ -104,9 +101,6 @@ const DECLINED: &[(&str, &str)] = &[ ("", "GatherElements"), // output shape == indices shape ("", "Size"), // scalar // Contrib ops that need a real rule written. - ("com.microsoft", "Attention"), // packed QKV; a different signature from - // ai.onnx::Attention, so the opset-23 arm - // deliberately does not cover it // Qwen3.5 / Qwen3-Next hybrid linear-attention primitives, read // from exported models rather than produced by our fusion passes. // ORT has no kernel for these at all, so handing them over does not @@ -114,9 +108,6 @@ const DECLINED: &[(&str, &str)] = &[ ("", "LinearAttention"), ("com.microsoft", "CausalConvWithState"), ("com.microsoft", "LinearAttention"), - ("com.microsoft", "MoE"), - ("com.microsoft", "PackedMultiHeadAttention"), - ("com.microsoft", "QMoE"), ]; /// Ops registered only so a test can exercise the registry machinery. They are diff --git a/crates/onnx-runtime-ep-cpu/src/kernels/attention.rs b/crates/onnx-runtime-ep-cpu/src/kernels/attention.rs index ce56b8f9f0..70ac43a3ca 100644 --- a/crates/onnx-runtime-ep-cpu/src/kernels/attention.rs +++ b/crates/onnx-runtime-ep-cpu/src/kernels/attention.rs @@ -75,41 +75,66 @@ pub struct AttentionFactory { pub since_version: u32, } +/// Why this kernel cannot run a given `ai.onnx::Attention` node, if it cannot. +/// +/// Answered at *claim* time: a rejection raised in the factory instead arrives +/// after ORT has compiled the node onto this EP and is a hard session failure +/// that no fallback recovers from. Implemented by running the factory's own +/// attribute parse, so the two cannot disagree. `since_version` selects opset +/// semantics inside `execute` and cannot change which nodes are rejected, so +/// the claim-time call passes the newest. +pub(crate) fn unsupported_reason(node: &Node) -> Option { + attributes_from_node(node, 26).err().map(|e| e.to_string()) +} + +fn attributes_from_node(node: &Node, since_version: u32) -> Result { + // Defensive, not load-bearing: ORT does not stamp this attribute (a + // real session was instrumented to confirm it arrives absent). But both + // ORT's kernels and ours read an explicit `scale = 0` as "use + // 1/sqrt(head_size)" rather than literally, so a zero taken at face + // value would zero every score silently. `> 0` is deliberately broader + // than ORT's `== 0`, since a negative scale is meaningless. + let scale = node + .attr("scale") + .and_then(|a| a.as_float()) + .filter(|s| *s > 0.0); + let is_causal = node.attr("is_causal").and_then(|a| a.as_int()).unwrap_or(0) != 0; + let q_num_heads = node + .attr("q_num_heads") + .and_then(|a| a.as_int()) + .map(|v| v as usize); + let kv_num_heads = node + .attr("kv_num_heads") + .and_then(|a| a.as_int()) + .map(|v| v as usize); + let qk_matmul_output_mode = node + .attr("qk_matmul_output_mode") + .and_then(|a| a.as_int()) + .unwrap_or(0); + let softcap = node + .attr("softcap") + .and_then(|a| a.as_float()) + .unwrap_or(0.0); + if !(0..=3).contains(&qk_matmul_output_mode) { + return Err(EpError::KernelFailed(format!( + "Attention: qk_matmul_output_mode {qk_matmul_output_mode} is not supported \ + (only 0, 1, 2, 3 are implemented)" + ))); + } + Ok(AttentionKernel { + scale, + is_causal, + q_num_heads, + kv_num_heads, + qk_matmul_output_mode, + softcap, + since_version, + }) +} + impl KernelFactory for AttentionFactory { fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { - let scale = node.attr("scale").and_then(|a| a.as_float()); - let is_causal = node.attr("is_causal").and_then(|a| a.as_int()).unwrap_or(0) != 0; - let q_num_heads = node - .attr("q_num_heads") - .and_then(|a| a.as_int()) - .map(|v| v as usize); - let kv_num_heads = node - .attr("kv_num_heads") - .and_then(|a| a.as_int()) - .map(|v| v as usize); - let qk_matmul_output_mode = node - .attr("qk_matmul_output_mode") - .and_then(|a| a.as_int()) - .unwrap_or(0); - let softcap = node - .attr("softcap") - .and_then(|a| a.as_float()) - .unwrap_or(0.0); - if !(0..=3).contains(&qk_matmul_output_mode) { - return Err(EpError::KernelFailed(format!( - "Attention: qk_matmul_output_mode {qk_matmul_output_mode} is not supported \ - (only 0, 1, 2, 3 are implemented)" - ))); - } - Ok(Box::new(AttentionKernel { - scale, - is_causal, - q_num_heads, - kv_num_heads, - qk_matmul_output_mode, - softcap, - since_version: self.since_version, - })) + Ok(Box::new(attributes_from_node(node, self.since_version)?)) } } diff --git a/crates/onnx-runtime-ep-cpu/src/kernels/group_query_attention.rs b/crates/onnx-runtime-ep-cpu/src/kernels/group_query_attention.rs index 596b7ebd49..ccfb2ac36c 100644 --- a/crates/onnx-runtime-ep-cpu/src/kernels/group_query_attention.rs +++ b/crates/onnx-runtime-ep-cpu/src/kernels/group_query_attention.rs @@ -598,90 +598,132 @@ pub struct GroupQueryAttentionKernel { pub struct GroupQueryAttentionFactory; -impl KernelFactory for GroupQueryAttentionFactory { - fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { - let required_heads = |name: &str| -> Result { - let value = node.attr(name).and_then(|a| a.as_int()).ok_or_else(|| { - EpError::KernelFailed(format!( - "GroupQueryAttention: missing required `{name}` attribute" - )) - })?; - usize::try_from(value) - .ok() - .filter(|&v| v > 0) - .ok_or_else(|| { - EpError::KernelFailed(format!("GroupQueryAttention: `{name}` must be > 0")) - }) - }; - let num_heads = required_heads("num_heads")?; - let kv_num_heads = required_heads("kv_num_heads")?; - if !num_heads.is_multiple_of(kv_num_heads) { +/// Why this kernel cannot run a given `GroupQueryAttention` node, if it cannot. +/// +/// Must be answered at *claim* time. `GetCapability` consults only the dtype +/// and shape filters, so a rejection raised later, in the kernel factory, +/// arrives after ORT has compiled the node onto this EP and is a **hard session +/// failure that no fallback recovers from**. ORT's own CPU GQA kernel runs +/// several configurations this one does not yet implement — `smooth_softmax=1` +/// (Gemma-style), quantized KV caches, `qk_output` — so claiming and then +/// failing would take a model that works today and kill it at `CreateSession`. +/// +/// Implemented by running the factory's own attribute parse, so a limit cannot +/// be added to [`attributes_from_node`] without appearing here too. These are +/// capability answers, not performance ones: each is a kernel we owe, and none +/// of them is a range where we are merely slower. +pub(crate) fn unsupported_reason(node: &Node) -> Option { + attributes_from_node(node).err().map(|e| e.to_string()) +} + +/// Every attribute check the factory performs, in one place. +fn attributes_from_node(node: &Node) -> Result { + let required_heads = |name: &str| -> Result { + let value = node.attr(name).and_then(|a| a.as_int()).ok_or_else(|| { + EpError::KernelFailed(format!( + "GroupQueryAttention: missing required `{name}` attribute" + )) + })?; + usize::try_from(value) + .ok() + .filter(|&v| v > 0) + .ok_or_else(|| { + EpError::KernelFailed(format!("GroupQueryAttention: `{name}` must be > 0")) + }) + }; + let num_heads = required_heads("num_heads")?; + let kv_num_heads = required_heads("kv_num_heads")?; + if !num_heads.is_multiple_of(kv_num_heads) { + return Err(EpError::KernelFailed(format!( + "GroupQueryAttention: num_heads {num_heads} must be a multiple of kv_num_heads {kv_num_heads}" + ))); + } + + for name in ["k_quant_type", "v_quant_type"] { + if let Some(value) = node.attr(name) + && value.as_str() != Some("NONE") + { return Err(EpError::KernelFailed(format!( - "GroupQueryAttention: num_heads {num_heads} must be a multiple of kv_num_heads {kv_num_heads}" + "GroupQueryAttention: `{name}` other than NONE is not yet supported by the f32 CPU kernel" ))); } + } + if node + .attr("kv_cache_bit_width") + .and_then(|a| a.as_int()) + .unwrap_or(0) + != 0 + { + return Err(EpError::KernelFailed( + "GroupQueryAttention: quantized KV cache is not yet supported".into(), + )); + } + if node.attr("qk_output").and_then(|a| a.as_int()).unwrap_or(0) != 0 { + return Err(EpError::KernelFailed( + "GroupQueryAttention: qk_output is not yet supported".into(), + )); + } + // ORT's schema default for this attribute is -1, not 0, and ORT + // materialises schema defaults onto the node before an EP ever sees it + // -- so every GQA node from a real ORT session arrives carrying + // `smooth_softmax = -1`. Its own kernel enables the feature only for + // the exact value 1 (`use_smooth_softmax_ = ... == 1`), so anything + // else means "off". Testing `!= 0` here rejected every ORT-resolved GQA + // node, which is why `plugin_ort_e2e`'s GQA assignment fixture is the + // regression test for this line. + let smooth_softmax = node + .attr("smooth_softmax") + .and_then(|a| a.as_int()) + .unwrap_or(0); + if smooth_softmax == 1 { + return Err(EpError::KernelFailed( + "GroupQueryAttention: smooth_softmax is not yet supported".into(), + )); + } - for name in ["k_quant_type", "v_quant_type"] { - if let Some(value) = node.attr(name) - && value.as_str() != Some("NONE") - { - return Err(EpError::KernelFailed(format!( - "GroupQueryAttention: `{name}` other than NONE is not yet supported by the f32 CPU kernel" - ))); - } - } - if node - .attr("kv_cache_bit_width") + let softcap = node + .attr("softcap") + .and_then(|a| a.as_float()) + .unwrap_or(0.0); + if softcap < 0.0 { + return Err(EpError::KernelFailed( + "GroupQueryAttention: softcap must be non-negative".into(), + )); + } + + Ok(GroupQueryAttentionKernel { + num_heads, + kv_num_heads, + // Defensive, not load-bearing: ORT does *not* stamp this + // attribute (it is OPTIONAL_VALUE with no schema default, and a + // real session was instrumented to confirm it arrives as absent). + // But both ORT's kernels and ours read an explicit `scale = 0` as + // "use 1/sqrt(head_size)" rather than literally, so a zero taken at + // face value would multiply every score by zero and return a + // silently wrong answer. `> 0` is deliberately broader than ORT's + // `== 0`: a negative scale is meaningless and would otherwise be + // honoured. + scale: node + .attr("scale") + .and_then(|a| a.as_float()) + .filter(|s| *s > 0.0), + do_rotary: node.attr("do_rotary").and_then(|a| a.as_int()).unwrap_or(0) != 0, + rotary_interleaved: node + .attr("rotary_interleaved") .and_then(|a| a.as_int()) .unwrap_or(0) - != 0 - { - return Err(EpError::KernelFailed( - "GroupQueryAttention: quantized KV cache is not yet supported".into(), - )); - } - if node.attr("qk_output").and_then(|a| a.as_int()).unwrap_or(0) != 0 { - return Err(EpError::KernelFailed( - "GroupQueryAttention: qk_output is not yet supported".into(), - )); - } - if node - .attr("smooth_softmax") + != 0, + local_window_size: node + .attr("local_window_size") .and_then(|a| a.as_int()) - .unwrap_or(0) - != 0 - { - return Err(EpError::KernelFailed( - "GroupQueryAttention: smooth_softmax is not yet supported".into(), - )); - } - - let softcap = node - .attr("softcap") - .and_then(|a| a.as_float()) - .unwrap_or(0.0); - if softcap < 0.0 { - return Err(EpError::KernelFailed( - "GroupQueryAttention: softcap must be non-negative".into(), - )); - } + .unwrap_or(-1), + softcap, + }) +} - Ok(Box::new(GroupQueryAttentionKernel { - num_heads, - kv_num_heads, - scale: node.attr("scale").and_then(|a| a.as_float()), - do_rotary: node.attr("do_rotary").and_then(|a| a.as_int()).unwrap_or(0) != 0, - rotary_interleaved: node - .attr("rotary_interleaved") - .and_then(|a| a.as_int()) - .unwrap_or(0) - != 0, - local_window_size: node - .attr("local_window_size") - .and_then(|a| a.as_int()) - .unwrap_or(-1), - softcap, - })) +impl KernelFactory for GroupQueryAttentionFactory { + fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { + Ok(Box::new(attributes_from_node(node)?)) } } diff --git a/crates/onnx-runtime-ep-cpu/src/kernels/mod.rs b/crates/onnx-runtime-ep-cpu/src/kernels/mod.rs index 751ccbfed6..2b98111eb5 100644 --- a/crates/onnx-runtime-ep-cpu/src/kernels/mod.rs +++ b/crates/onnx-runtime-ep-cpu/src/kernels/mod.rs @@ -163,6 +163,9 @@ static U8_ONLY: &[DataType] = &[DataType::Uint8]; static I32_ONLY: &[DataType] = &[DataType::Int32]; +/// Integer index/length inputs, matching `to_dense_i64`'s acceptance set. +static INDEX_DTYPES: &[DataType] = &[DataType::Int64, DataType::Int32]; + /// Per-input-slot dtype constraints for a mixed-dtype op. /// /// `supported_dtypes_for_op` returns the *union* of the dtypes on an op's @@ -178,6 +181,19 @@ static I32_ONLY: &[DataType] = &[DataType::Int32]; /// against its own set instead of the union; absent slots and slots not listed /// keep the union rule. They must stay in step with the `require_dtype` calls /// in the corresponding kernel's `execute`. +/// +/// The same mechanism also fixes the *opposite* failure, which is the more +/// damaging one. `supported_dtypes_for_op` returns `FLOAT_DTYPES` for the +/// attention family, because that is what their tensor edges carry -- but +/// `RotaryEmbedding`'s `position_ids` is int64, and +/// `GroupQueryAttention`'s `seqlens_k` / `total_sequence_length` are int32. The +/// union test then rejects those slots and the node is silently handed to ORT's +/// CPU EP, even though our kernel runs it. That contradicts this EP's contract +/// (see `provider.rs::claim_preference_node`): a node we can execute is never +/// delegated. Listing the integer slots explicitly is what makes the two most +/// important decode ops in the engine reachable at all; +/// `plugin_ort_e2e.rs::no_supported_node_is_ever_left_to_the_ort_cpu_ep` is the +/// falsifier, and it caught both of them. pub fn input_dtype_constraints_for_op( op_type: &str, domain: &str, @@ -202,9 +218,50 @@ pub fn input_dtype_constraints_for_op( (6, F32_ONLY), (7, QUANTIZED_STORAGE_DTYPES), ]; + // X, position_ids, cos_cache, sin_cache. + static MSFT_ROTARY_SLOTS: &[(usize, &[DataType])] = &[(1, INDEX_DTYPES)]; + // X, cos_cache, sin_cache, position_ids. + static ONNX_ROTARY_SLOTS: &[(usize, &[DataType])] = &[(3, INDEX_DTYPES)]; + // query, key, value, past_key, past_value, seqlens_k, total_sequence_length, + // cos_cache, sin_cache, position_ids. `seqlens_k` is strictly int32 (the + // kernel rejects anything else); the other two go through `to_dense_i64`. + // Slot 9 is optional and only present with `do_rotary`, but leaving it + // unlisted made a `do_rotary` GQA node with explicit int64 positions fail + // the float union and go to ORT -- the exact bug this table exists to stop. + static GQA_SLOTS: &[(usize, &[DataType])] = + &[(5, I32_ONLY), (6, INDEX_DTYPES), (9, INDEX_DTYPES)]; + // query, key, value, bias, key_padding_mask, attention_bias, past_key, past_value. + static MHA_SLOTS: &[(usize, &[DataType])] = &[(4, INDEX_DTYPES)]; + // input, weights, bias, mask_index, past, attention_bias. + static MSFT_ATTENTION_SLOTS: &[(usize, &[DataType])] = &[(3, INDEX_DTYPES)]; + // query, key, value, bias, token_offset, cumulative_sequence_length. + // The last two are strictly int32 (`packed_multi_head_attention.rs` + // rejects anything else). ORT has no CPU kernel for this op, so failing + // the union here does not fall back -- it fails session creation. + static PACKED_MHA_SLOTS: &[(usize, &[DataType])] = &[(4, I32_ONLY), (5, I32_ONLY)]; + // QMoE stores its experts int4/int8-packed in uint8, so the float union + // rejects the weight and zero-point slots outright. Slot order: + // input, router_probs, fc1_w, fc1_scales, fc1_bias, fc2_w, fc2_scales, + // fc2_bias, fc3_w, fc3_scales, fc3_bias, fc1_zp, fc2_zp, fc3_zp, + // router_weights. Mirrors the `require_dtype` calls in `qmoe.rs`. + static QMOE_SLOTS: &[(usize, &[DataType])] = &[ + (2, U8_ONLY), + (5, U8_ONLY), + (8, U8_ONLY), + (11, U8_ONLY), + (12, U8_ONLY), + (13, U8_ONLY), + ]; match (op_type, domain) { ("MatMulNBits", "com.microsoft") => MATMUL_NBITS_SLOTS, ("QLinearMatMul", "") => QLINEAR_MATMUL_SLOTS, + ("RotaryEmbedding", "com.microsoft") => MSFT_ROTARY_SLOTS, + ("RotaryEmbedding", "") => ONNX_ROTARY_SLOTS, + ("GroupQueryAttention", "com.microsoft") => GQA_SLOTS, + ("MultiHeadAttention", "com.microsoft") => MHA_SLOTS, + ("Attention", "com.microsoft") => MSFT_ATTENTION_SLOTS, + ("PackedMultiHeadAttention", "com.microsoft") => PACKED_MHA_SLOTS, + ("QMoE", "com.microsoft") => QMOE_SLOTS, _ => &[], } } @@ -389,7 +446,13 @@ pub fn supported_dtypes_for_op(op_type: &str, domain: &str) -> &'static [DataTyp ("SimplifiedLayerNormalization", "") | ("LinearAttention", "") => FLOAT_DTYPES, ("MatMulNBits", "com.microsoft") => MATMUL_NBITS_DTYPES, - ("MoE", "com.microsoft") | ("QMoE", "com.microsoft") => F32_ONLY, + // `moe.rs` widens f16/bf16 to f32, computes, and narrows on the way + // out, so advertising f32 alone declined every realistic MoE node -- + // production mixtures are exported in half precision. `QMoE` is f32 in + // and out with the experts carried as packed uint8, whose slots are + // listed in `input_dtype_constraints_for_op`. + ("MoE", "com.microsoft") => FLOAT_COMPUTE_DTYPES, + ("QMoE", "com.microsoft") => F32_ONLY, // pkg.nxrt custom ops: f32-only (fail closed). (_, "pkg.nxrt") => F32_ONLY, diff --git a/crates/onnx-runtime-ep-cpu/src/kernels/moe.rs b/crates/onnx-runtime-ep-cpu/src/kernels/moe.rs index 42d50ae6aa..ba03eb5eca 100644 --- a/crates/onnx-runtime-ep-cpu/src/kernels/moe.rs +++ b/crates/onnx-runtime-ep-cpu/src/kernels/moe.rs @@ -46,15 +46,39 @@ pub(super) struct MoeAttributes { swiglu_limit: f32, } +/// Every attribute check `MoEFactory::create` performs, in one place so that +/// [`unsupported_reason`] and the factory cannot disagree. +fn float_moe_attributes(node: &Node) -> Result { + let attributes = MoeAttributes::from_node(node)?; + if int_attr(node, "block_size", 0)? != 0 { + return Err(error( + "block_size is a QMoE quantization attribute and is unsupported by float MoE", + )); + } + Ok(attributes) +} + +/// Why this kernel cannot run a given `MoE` node, if it cannot. +/// +/// Must be answered at *claim* time. `GetCapability` consults only the dtype +/// and shape filters, so a rejection raised later, in the kernel factory, +/// arrives after ORT has compiled the node onto this EP and is a **hard session +/// failure that no fallback recovers from**. ORT's own CPU `MoE` kernel runs +/// configurations this one does not yet implement — `use_sparse_mixer=1` most +/// notably, which real sparse-mixer mixtures set — so claiming and then failing +/// would take a model that works today and kill it at `CreateSession`. +/// +/// Implemented by running the factory's own attribute parse, so a limit cannot +/// be added to `create` without appearing here too. +pub(crate) fn unsupported_reason(node: &Node) -> Option { + float_moe_attributes(node).err().map(|e| e.to_string()) +} + impl KernelFactory for MoEFactory { fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { - let attributes = MoeAttributes::from_node(node)?; - if int_attr(node, "block_size", 0)? != 0 { - return Err(error( - "block_size is a QMoE quantization attribute and is unsupported by float MoE", - )); - } - Ok(Box::new(MoEKernel { attributes })) + Ok(Box::new(MoEKernel { + attributes: float_moe_attributes(node)?, + })) } } diff --git a/crates/onnx-runtime-ep-cpu/src/kernels/msft_attention.rs b/crates/onnx-runtime-ep-cpu/src/kernels/msft_attention.rs index 4ac5c04b3e..03f46dfb86 100644 --- a/crates/onnx-runtime-ep-cpu/src/kernels/msft_attention.rs +++ b/crates/onnx-runtime-ep-cpu/src/kernels/msft_attention.rs @@ -72,88 +72,112 @@ pub struct MsftAttentionKernel { /// Factory for [`MsftAttentionKernel`], reading the contrib-op attributes. pub struct MsftAttentionFactory; -impl KernelFactory for MsftAttentionFactory { - fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { - let num_heads = node - .attr("num_heads") - .and_then(|a| a.as_int()) - .ok_or_else(|| { - EpError::KernelFailed("Attention: missing required `num_heads` attribute".into()) - })?; - if num_heads <= 0 { - return Err(EpError::KernelFailed(format!( - "Attention: num_heads must be > 0, got {num_heads}" - ))); - } +/// Why this kernel cannot run a given `com.microsoft::Attention` node, if it +/// cannot. +/// +/// Answered at *claim* time: a rejection raised in the factory instead arrives +/// after ORT has compiled the node onto this EP and is a hard session failure +/// that no fallback recovers from. Implemented by running the factory's own +/// attribute parse, so the two cannot disagree. +pub(crate) fn unsupported_reason(node: &Node) -> Option { + attributes_from_node(node).err().map(|e| e.to_string()) +} - // Unsupported variants are rejected up front (typed error, not a panic). - if node.attr("do_rotary").and_then(|a| a.as_int()).unwrap_or(0) == 1 { - return Err(EpError::KernelFailed( - "Attention: `do_rotary` is not supported (fuse MHA + RotaryEmbedding instead, as \ +fn attributes_from_node(node: &Node) -> Result { + let num_heads = node + .attr("num_heads") + .and_then(|a| a.as_int()) + .ok_or_else(|| { + EpError::KernelFailed("Attention: missing required `num_heads` attribute".into()) + })?; + if num_heads <= 0 { + return Err(EpError::KernelFailed(format!( + "Attention: num_heads must be > 0, got {num_heads}" + ))); + } + + // Unsupported variants are rejected up front (typed error, not a panic). + if node.attr("do_rotary").and_then(|a| a.as_int()).unwrap_or(0) == 1 { + return Err(EpError::KernelFailed( + "Attention: `do_rotary` is not supported (fuse MHA + RotaryEmbedding instead, as \ ORT's CPU kernel requires)" - .into(), - )); - } - if node - .attr("past_present_share_buffer") - .and_then(|a| a.as_int()) - .unwrap_or(0) - == 1 - { - return Err(EpError::KernelFailed( - "Attention: `past_present_share_buffer` (DecoderMaskedMHA buffer sharing) is not \ + .into(), + )); + } + if node + .attr("past_present_share_buffer") + .and_then(|a| a.as_int()) + .unwrap_or(0) + == 1 + { + return Err(EpError::KernelFailed( + "Attention: `past_present_share_buffer` (DecoderMaskedMHA buffer sharing) is not \ supported" - .into(), - )); - } + .into(), + )); + } - let scale = node.attr("scale").and_then(|a| a.as_float()); - let mask_filter_value = node - .attr("mask_filter_value") - .and_then(|a| a.as_float()) - .unwrap_or(-10000.0); - let unidirectional = node - .attr("unidirectional") - .and_then(|a| a.as_int()) - .unwrap_or(0) - == 1; - - let qkv_hidden_sizes = match node.attr("qkv_hidden_sizes").and_then(|a| a.as_ints()) { - Some(sizes) => { - if sizes.len() != 3 { + // Defensive, not load-bearing: ORT does not stamp this attribute (a + // real session was instrumented to confirm it arrives absent). But both + // ORT's kernels and ours read an explicit `scale = 0` as "use + // 1/sqrt(head_size)" rather than literally, so a zero taken at face + // value would zero every score silently. `> 0` is deliberately broader + // than ORT's `== 0`, since a negative scale is meaningless. + let scale = node + .attr("scale") + .and_then(|a| a.as_float()) + .filter(|s| *s > 0.0); + let mask_filter_value = node + .attr("mask_filter_value") + .and_then(|a| a.as_float()) + .unwrap_or(-10000.0); + let unidirectional = node + .attr("unidirectional") + .and_then(|a| a.as_int()) + .unwrap_or(0) + == 1; + + let qkv_hidden_sizes = match node.attr("qkv_hidden_sizes").and_then(|a| a.as_ints()) { + Some(sizes) => { + if sizes.len() != 3 { + return Err(EpError::KernelFailed(format!( + "Attention: qkv_hidden_sizes must have 3 elements, got {}", + sizes.len() + ))); + } + let n = num_heads; + for s in sizes { + if *s <= 0 || s % n != 0 { return Err(EpError::KernelFailed(format!( - "Attention: qkv_hidden_sizes must have 3 elements, got {}", - sizes.len() - ))); - } - let n = num_heads; - for s in sizes { - if *s <= 0 || s % n != 0 { - return Err(EpError::KernelFailed(format!( - "Attention: qkv_hidden_sizes element {s} must be > 0 and divisible by \ + "Attention: qkv_hidden_sizes element {s} must be > 0 and divisible by \ num_heads {num_heads}" - ))); - } + ))); } - if sizes[0] != sizes[1] { - return Err(EpError::KernelFailed( - "Attention: qkv_hidden_sizes[0] (q_hidden) must equal qkv_hidden_sizes[1] \ + } + if sizes[0] != sizes[1] { + return Err(EpError::KernelFailed( + "Attention: qkv_hidden_sizes[0] (q_hidden) must equal qkv_hidden_sizes[1] \ (k_hidden)" - .into(), - )); - } - Some([sizes[0] as usize, sizes[1] as usize, sizes[2] as usize]) + .into(), + )); } - None => None, - }; + Some([sizes[0] as usize, sizes[1] as usize, sizes[2] as usize]) + } + None => None, + }; - Ok(Box::new(MsftAttentionKernel { - num_heads: num_heads as usize, - scale, - mask_filter_value, - unidirectional, - qkv_hidden_sizes, - })) + Ok(MsftAttentionKernel { + num_heads: num_heads as usize, + scale, + mask_filter_value, + unidirectional, + qkv_hidden_sizes, + }) +} + +impl KernelFactory for MsftAttentionFactory { + fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { + Ok(Box::new(attributes_from_node(node)?)) } } diff --git a/crates/onnx-runtime-ep-cpu/src/kernels/multi_head_attention.rs b/crates/onnx-runtime-ep-cpu/src/kernels/multi_head_attention.rs index 979f307c11..487aed6758 100644 --- a/crates/onnx-runtime-ep-cpu/src/kernels/multi_head_attention.rs +++ b/crates/onnx-runtime-ep-cpu/src/kernels/multi_head_attention.rs @@ -80,37 +80,60 @@ pub struct MultiHeadAttentionKernel { /// Factory for [`MultiHeadAttentionKernel`], reading the contrib-op attributes. pub struct MultiHeadAttentionFactory; +/// Why this kernel cannot run a given `MultiHeadAttention` node, if it cannot. +/// +/// Answered at *claim* time: a rejection raised in the factory instead arrives +/// after ORT has compiled the node onto this EP and is a hard session failure +/// that no fallback recovers from. Implemented by running the factory's own +/// attribute parse, so the two cannot disagree. +pub(crate) fn unsupported_reason(node: &Node) -> Option { + attributes_from_node(node).err().map(|e| e.to_string()) +} + +fn attributes_from_node(node: &Node) -> Result { + let num_heads = node + .attr("num_heads") + .and_then(|a| a.as_int()) + .ok_or_else(|| { + EpError::KernelFailed( + "MultiHeadAttention: missing required `num_heads` attribute".into(), + ) + })?; + if num_heads <= 0 { + return Err(EpError::KernelFailed(format!( + "MultiHeadAttention: num_heads must be > 0, got {num_heads}" + ))); + } + // Defensive, not load-bearing: ORT does not stamp this attribute (a + // real session was instrumented to confirm it arrives absent). But both + // ORT's kernels and ours read an explicit `scale = 0` as "use + // 1/sqrt(head_size)" rather than literally, so a zero taken at face + // value would zero every score silently. `> 0` is deliberately broader + // than ORT's `== 0`, since a negative scale is meaningless. + let scale = node + .attr("scale") + .and_then(|a| a.as_float()) + .filter(|s| *s > 0.0); + let mask_filter_value = node + .attr("mask_filter_value") + .and_then(|a| a.as_float()) + .unwrap_or(-10000.0); + let unidirectional = node + .attr("unidirectional") + .and_then(|a| a.as_int()) + .unwrap_or(0) + == 1; + Ok(MultiHeadAttentionKernel { + num_heads: num_heads as usize, + scale, + mask_filter_value, + unidirectional, + }) +} + impl KernelFactory for MultiHeadAttentionFactory { fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { - let num_heads = node - .attr("num_heads") - .and_then(|a| a.as_int()) - .ok_or_else(|| { - EpError::KernelFailed( - "MultiHeadAttention: missing required `num_heads` attribute".into(), - ) - })?; - if num_heads <= 0 { - return Err(EpError::KernelFailed(format!( - "MultiHeadAttention: num_heads must be > 0, got {num_heads}" - ))); - } - let scale = node.attr("scale").and_then(|a| a.as_float()); - let mask_filter_value = node - .attr("mask_filter_value") - .and_then(|a| a.as_float()) - .unwrap_or(-10000.0); - let unidirectional = node - .attr("unidirectional") - .and_then(|a| a.as_int()) - .unwrap_or(0) - == 1; - Ok(Box::new(MultiHeadAttentionKernel { - num_heads: num_heads as usize, - scale, - mask_filter_value, - unidirectional, - })) + Ok(Box::new(attributes_from_node(node)?)) } } diff --git a/crates/onnx-runtime-ep-cpu/src/kernels/packed_multi_head_attention.rs b/crates/onnx-runtime-ep-cpu/src/kernels/packed_multi_head_attention.rs index 25ed36e173..ffa2d2ca3e 100644 --- a/crates/onnx-runtime-ep-cpu/src/kernels/packed_multi_head_attention.rs +++ b/crates/onnx-runtime-ep-cpu/src/kernels/packed_multi_head_attention.rs @@ -28,31 +28,50 @@ struct PackedMultiHeadAttentionKernel { scale: Option, } +/// Why this kernel cannot run a given `PackedMultiHeadAttention` node, if it +/// cannot. +/// +/// Answered at *claim* time: a rejection raised in the factory instead arrives +/// after ORT has compiled the node onto this EP and is a hard session failure. +/// ORT has no CPU kernel for this op at all, so a decline here fails the load +/// too — but with ORT's own `NOT_IMPLEMENTED` message rather than ours, and +/// without the EP having claimed a node it cannot run. Implemented by running +/// the factory's own attribute parse, so the two cannot disagree. +pub(crate) fn unsupported_attribute_reason(node: &Node) -> Option { + attributes_from_node(node).err().map(|e| e.to_string()) +} + +fn attributes_from_node(node: &Node) -> Result { + let num_heads = node + .attr("num_heads") + .and_then(|attribute| attribute.as_int()) + .ok_or_else(|| error("missing required int attribute 'num_heads'"))?; + let num_heads = usize::try_from(num_heads) + .ok() + .filter(|value| *value > 0) + .ok_or_else(|| error(format!("num_heads must be positive, got {num_heads}")))?; + let scale = node + .attr("scale") + .map(|attribute| { + attribute + .as_float() + .ok_or_else(|| error("scale attribute must be a float")) + }) + .transpose()?; + if scale.is_some_and(|value| !value.is_finite()) { + return Err(error("scale must be finite")); + } + // Same rule as the other four attention kernels: an explicit + // non-positive scale means "use 1/sqrt(head_size)", not "multiply every + // score by zero". This op became reachable at the same time as they + // did, so it gets the same guard rather than a silently different one. + let scale = scale.filter(|value| *value > 0.0); + Ok(PackedMultiHeadAttentionKernel { num_heads, scale }) +} + impl KernelFactory for PackedMultiHeadAttentionFactory { fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { - let num_heads = node - .attr("num_heads") - .and_then(|attribute| attribute.as_int()) - .ok_or_else(|| error("missing required int attribute 'num_heads'"))?; - let num_heads = usize::try_from(num_heads) - .ok() - .filter(|value| *value > 0) - .ok_or_else(|| error(format!("num_heads must be positive, got {num_heads}")))?; - let scale = node - .attr("scale") - .map(|attribute| { - attribute - .as_float() - .ok_or_else(|| error("scale attribute must be a float")) - }) - .transpose()?; - if scale.is_some_and(|value| !value.is_finite()) { - return Err(error("scale must be finite")); - } - Ok(Box::new(PackedMultiHeadAttentionKernel { - num_heads, - scale, - })) + Ok(Box::new(attributes_from_node(node)?)) } } @@ -153,13 +172,8 @@ pub fn unsupported_reason( "cumulative_sequence_length length {cumulative_length} must equal token_offset batch {batch} + 1" )); } - if let Some(attribute) = node.attr("scale") { - let Some(scale) = attribute.as_float() else { - return fail("scale attribute must be a float".into()); - }; - if !scale.is_finite() { - return fail("scale must be finite".into()); - } + if let Some(reason) = unsupported_attribute_reason(node) { + return fail(reason); } None } diff --git a/crates/onnx-runtime-ep-cpu/src/kernels/qmoe.rs b/crates/onnx-runtime-ep-cpu/src/kernels/qmoe.rs index 1a6cef72b1..5aa8d73edc 100644 --- a/crates/onnx-runtime-ep-cpu/src/kernels/qmoe.rs +++ b/crates/onnx-runtime-ep-cpu/src/kernels/qmoe.rs @@ -67,37 +67,68 @@ pub struct QMoEKernel { prefetch_experts: bool, } +/// Why this kernel cannot run a given `QMoE` node, if it cannot. +/// +/// This has to be answered at *claim* time, not in `create`. `GetCapability` +/// consults only the dtype and shape filters; a rejection raised later, in the +/// kernel factory, arrives after ORT has already compiled the node onto this EP +/// and is a **hard session failure that no fallback recovers from** — strictly +/// worse for the user than never claiming the node. +/// +/// The sharpest case is column-wise quantization. ORT's schema leaves +/// `block_size` with no default, so an absent attribute means one scale per +/// output row (`[experts, out_features]`), while this kernel implements only +/// the blocked form (`[experts, out_features, in_features / block_size]`, block +/// a power of two ≥ 16). ORT's own CPU kernel *does* run the column-wise form, +/// so claiming and then failing would take a working model and kill it. The +/// same is true of `use_sparse_mixer=1` and of non-integer `quant_type`. +/// Declining here is a capability answer, not a performance one; the fix is to +/// implement the missing paths, tracked in the benchmark doc's §23.6. +/// +/// Implemented by running the factory's own attribute parse, so a limit cannot +/// be added to `create` without appearing here too. +pub(crate) fn unsupported_reason(node: &Node) -> Option { + quantized_moe_attributes(node).err().map(|e| e.to_string()) +} + +/// Everything `QMoEFactory::create` validates, in one place so that +/// [`unsupported_reason`] and the factory cannot disagree. +fn quantized_moe_attributes(node: &Node) -> Result<(MoeAttributes, usize, usize)> { + let attributes = MoeAttributes::from_node(node)?; + let bits = int_attr(node, "expert_weight_bits", 4)?; + if !matches!(bits, 1 | 2 | 4 | 8) { + return Err(error(format!( + "expert_weight_bits must be one of {{1, 2, 4, 8}}, got {bits}" + ))); + } + let block_size = int_attr(node, "block_size", 0)?; + if block_size < 16 || !(block_size as usize).is_power_of_two() { + return Err(error(format!( + "block_size must be a power of two and at least 16, got {block_size}" + ))); + } + let quant_type = match node.attr("quant_type") { + Some(attr) => attr + .as_str() + .ok_or_else(|| error("attribute quant_type must be a string"))?, + None => "int", + }; + if quant_type != "int" { + return Err(error(format!( + "quant_type='{quant_type}' is unsupported; this kernel implements integer affine QMoE only" + ))); + } + Ok((attributes, bits as usize, block_size as usize)) +} + impl KernelFactory for QMoEFactory { fn create(&self, node: &Node, _input_shapes: &[Vec]) -> Result> { - let attributes = MoeAttributes::from_node(node)?; - let bits = int_attr(node, "expert_weight_bits", 4)?; - if !matches!(bits, 1 | 2 | 4 | 8) { - return Err(error(format!( - "expert_weight_bits must be one of {{1, 2, 4, 8}}, got {bits}" - ))); - } - let block_size = int_attr(node, "block_size", 0)?; - if block_size < 16 || !(block_size as usize).is_power_of_two() { - return Err(error(format!( - "block_size must be a power of two and at least 16, got {block_size}" - ))); - } - let quant_type = match node.attr("quant_type") { - Some(attr) => attr - .as_str() - .ok_or_else(|| error("attribute quant_type must be a string"))?, - None => "int", - }; - if quant_type != "int" { - return Err(error(format!( - "quant_type='{quant_type}' is unsupported; this kernel implements integer affine QMoE only" - ))); - } + let (attributes, bits, block_size) = quantized_moe_attributes(node)?; Ok(Box::new(QMoEKernel { layer_id: node.id.0, attributes, - bits: bits as usize, - block_size: block_size as usize, + bits, + block_size, weight_offload: WeightOffloadMode::from_env(), host_cache: self.host_cache.clone(), prefetch_experts: weight_offload_prefetch_default(), diff --git a/crates/onnx-runtime-ep-cpu/src/provider.rs b/crates/onnx-runtime-ep-cpu/src/provider.rs index 1867617595..5611bc9c5a 100644 --- a/crates/onnx-runtime-ep-cpu/src/provider.rs +++ b/crates/onnx-runtime-ep-cpu/src/provider.rs @@ -463,6 +463,39 @@ impl ExecutionProvider for CpuExecutionProvider { { return KernelMatch::unsupported(reason); } + // Attribute-level capability limits for the attention and MoE family. + // + // Every one of these mirrors a rejection its kernel factory raises, and + // mirroring is mandatory rather than tidy: `GetCapability` consults only + // the dtype and shape filters, so a factory rejection arrives *after* + // ORT has compiled the node onto this EP and is a hard session failure + // that no fallback recovers from. Claiming an op we cannot configure is + // therefore strictly worse for the user than never claiming it — it + // turns a model that runs on ORT's CPU EP into one that does not load. + // + // Each `unsupported_reason` runs the factory's own attribute parse, so + // a new limit cannot be added to a factory without appearing here. + // + // These are *capability* declines, never performance ones: every entry + // is a kernel we owe. See the benchmark doc's §23.6. + let attribute_reason = match (op.op_type.as_str(), domain) { + ("MoE", "com.microsoft") => crate::kernels::moe::unsupported_reason(op), + ("QMoE", "com.microsoft") => crate::kernels::qmoe::unsupported_reason(op), + ("GroupQueryAttention", "com.microsoft") => { + crate::kernels::group_query_attention::unsupported_reason(op) + } + ("MultiHeadAttention", "com.microsoft") => { + crate::kernels::multi_head_attention::unsupported_reason(op) + } + ("Attention", "com.microsoft") => { + crate::kernels::msft_attention::unsupported_reason(op) + } + ("Attention", "ai.onnx") => crate::kernels::attention::unsupported_reason(op), + _ => None, + }; + if let Some(reason) = attribute_reason { + return KernelMatch::unsupported(reason); + } if op.op_type == "PackedMultiHeadAttention" && op.domain == "com.microsoft" && let Some(reason) = crate::kernels::packed_multi_head_attention::unsupported_reason( @@ -1125,6 +1158,185 @@ mod tests { assert!(ep.get_kernel(&bad, &[], 17).is_err()); } + /// Every attribute a kernel factory rejects must also be rejected by + /// `supports_op`, because a factory rejection lands after ORT has compiled + /// the node onto this EP and is a hard session failure no fallback recovers + /// from — strictly worse than never claiming. This is the pure-Rust half of + /// `plugin_ort_e2e::factory_only_capability_limits_are_declined_at_claim_time`, + /// so a regression is caught even where ORT is unavailable. + #[test] + fn every_factory_attribute_rejection_is_mirrored_at_claim_time() { + let ep = CpuExecutionProvider::new(); + /// `(op, domain, opset, rejecting attribute, prerequisite attributes)`. + type FactoryRejection = ( + &'static str, + &'static str, + u64, + (&'static str, Attribute), + &'static [(&'static str, Attribute)], + ); + let cases: &[FactoryRejection] = &[ + ( + "MoE", + "com.microsoft", + 1, + ("use_sparse_mixer", Attribute::Int(1)), + &[], + ), + ( + "QMoE", + "com.microsoft", + 1, + ("block_size", Attribute::Int(0)), + &[], + ), + ( + "QMoE", + "com.microsoft", + 1, + ("expert_weight_bits", Attribute::Int(3)), + &[("block_size", Attribute::Int(32))], + ), + ( + "GroupQueryAttention", + "com.microsoft", + 1, + ("smooth_softmax", Attribute::Int(1)), + &[ + ("num_heads", Attribute::Int(4)), + ("kv_num_heads", Attribute::Int(2)), + ], + ), + ( + "GroupQueryAttention", + "com.microsoft", + 1, + ("qk_output", Attribute::Int(1)), + &[ + ("num_heads", Attribute::Int(4)), + ("kv_num_heads", Attribute::Int(2)), + ], + ), + ( + "GroupQueryAttention", + "com.microsoft", + 1, + ("kv_cache_bit_width", Attribute::Int(8)), + &[ + ("num_heads", Attribute::Int(4)), + ("kv_num_heads", Attribute::Int(2)), + ], + ), + ( + "Attention", + "com.microsoft", + 1, + ("do_rotary", Attribute::Int(1)), + &[("num_heads", Attribute::Int(4))], + ), + ( + "Attention", + "com.microsoft", + 1, + ("past_present_share_buffer", Attribute::Int(1)), + &[("num_heads", Attribute::Int(4))], + ), + ( + "Attention", + "ai.onnx", + 23, + ("qk_matmul_output_mode", Attribute::Int(7)), + &[], + ), + ( + "MultiHeadAttention", + "com.microsoft", + 1, + ("num_heads", Attribute::Int(0)), + &[], + ), + ]; + + for (op, domain, opset, (attr, value), extra) in cases { + let mut node = Node::new(NodeId(0), *op, vec![], vec![]); + node.domain = (*domain).into(); + for (name, extra_value) in *extra { + node.attributes.insert((*name).into(), extra_value.clone()); + } + node.attributes.insert((*attr).into(), value.clone()); + + let factory = ep.get_kernel(&node, &[], *opset); + assert!( + factory.is_err(), + "{domain}::{op} with {attr} is expected to be a factory rejection; if the \ + kernel now implements it, drop this row rather than the claim-time guard" + ); + + let claim = ep.supports_op(&node, *opset, &[], &[], &[]); + assert!( + !claim.is_supported(), + "{domain}::{op} with {attr} is claimed but its factory rejects it, so the \ + session dies at CreateSession with no fallback. Mirror the limit in \ + `supports_op`." + ); + } + + // `PackedMultiHeadAttention` needs a fully-formed node rather than a + // table row. Its claim-time check inspects inputs, shapes and dtypes + // before it reaches the attribute mirror, so an attribute-only node + // declines for the *wrong* reason and the assertion would pass even + // with the mirror deleted — which is exactly what review demonstrated. + // Hence the explicit reason match. + let mut graph = Graph::new(); + let value = |graph: &mut Graph, name: &str, dtype| { + Some(graph.create_named_value(name.to_string(), dtype, static_shape([]))) + }; + let inputs = vec![ + value(&mut graph, "query", DataType::Float32), + value(&mut graph, "key", DataType::Float32), + value(&mut graph, "value", DataType::Float32), + None, + value(&mut graph, "token_offset", DataType::Int32), + value(&mut graph, "cumulative_sequence_length", DataType::Int32), + ]; + let output = + graph.create_named_value("output".to_string(), DataType::Float32, static_shape([])); + let mut node = Node::new(NodeId(0), "PackedMultiHeadAttention", inputs, vec![output]); + node.domain = "com.microsoft".into(); + node.attributes + .insert("num_heads".into(), Attribute::Int(2)); + node.attributes + .insert("scale".into(), Attribute::Float(f32::INFINITY)); + let shapes = [ + static_shape([4, 8]), + static_shape([4, 8]), + static_shape([4, 8]), + static_shape([]), + static_shape([1, 4]), + static_shape([2]), + ]; + let dtypes = [ + DataType::Float32, + DataType::Float32, + DataType::Float32, + DataType::Float32, + DataType::Int32, + DataType::Int32, + ]; + assert!( + ep.get_kernel(&node, &[], 1).is_err(), + "PackedMultiHeadAttention with a non-finite scale is expected to be a factory \ + rejection" + ); + let claim = ep.supports_op(&node, 1, &shapes, &dtypes, &[]); + let reason = claim.reason().unwrap_or_default().to_string(); + assert!( + reason.contains("scale must be finite"), + "PackedMultiHeadAttention's attribute mirror was not reached; the node declined \ + for `{reason}` instead, so this case would not catch the mirror being removed" + ); + } + #[test] fn supports_op_reflects_selected_operator_groups() { let ep = CpuExecutionProvider::new(); diff --git a/crates/onnx-runtime-ep-plugin/src/compute.rs b/crates/onnx-runtime-ep-plugin/src/compute.rs index 2d4f675319..ab9ce44cf7 100644 --- a/crates/onnx-runtime-ep-plugin/src/compute.rs +++ b/crates/onnx-runtime-ep-plugin/src/compute.rs @@ -132,6 +132,27 @@ pub enum ShapeInference { }, /// RotaryEmbedding — output same shape as input[0]. RotaryEmbedding, + /// `com.microsoft::Attention`: packed-QKV attention. + /// + /// A different signature from [`Self::AttentionStd`], which is why the + /// opset-23 `ai.onnx::Attention` arm cannot cover it. Here input(0) is the + /// *unprojected* activation `(batch, seq, input_hidden)` and input(1) is + /// the fused projection weight `(input_hidden, q_hidden + k_hidden + + /// v_hidden)`, so the output width is `v_hidden` — not the input's last + /// dim, and not `weights.dim1`. + MsftAttention { + /// `qkv_hidden_sizes[2]` when the attribute is present, else + /// `weights.dim1 / 3` resolved from the input shape at Compute time. + v_hidden: Option, + num_heads: usize, + num_outputs: usize, + }, + /// `com.microsoft::PackedMultiHeadAttention`: output + /// `[token_count, v_hidden]` = `[input[0].dim0, input[2].dim1]`. + /// + /// Tokens are packed across the batch with no padding, so the output is + /// rank-2 and neither dim can be read off input[0] alone. + PackedMultiHeadAttention, /// Standard `ai.onnx::Attention` (opset 23+): scaled dot-product attention. /// /// Q/K/V are rank-3 `(batch, seq, hidden)` or rank-4 @@ -242,6 +263,21 @@ impl ShapeInference { | "BitCount" | "Bernoulli" | "NegativeLogLikelihoodLoss" + // Shape-preserving ops on the attention / MoE / KV-cache path. + // Every one is registered by the CPU EP, and every one was absent + // here, so `GetCapability`'s fail-closed shape filter dropped the + // claim and ORT's CPU EP ran it -- the same silent mechanism that + // hid the activations until #1082 and the trigonometrics until + // #1097. `PackedMultiHeadAttention` is the sharpest case -- ORT + // has no CPU kernel for it at all, so giving it away cost a load + // failure rather than a slower run -- but ORT does run `MoE` and + // `QMoE` on CPU, so for those the decline was simply us not + // executing an op we implement. + | "MoE" + | "QMoE" + | "ScatterND" + | "ScatterElements" + | "Trilu" | "Clip" => Self::SameAsInput(0), // ── Shape-preserving normalisation ops ─────────────────────── @@ -289,6 +325,7 @@ impl ShapeInference { "Reshape" => Self::ReshapeData { allowzero: false }, "Slice" => Self::SliceData, "RotaryEmbedding" => Self::RotaryEmbedding, + "PackedMultiHeadAttention" => Self::PackedMultiHeadAttention, // ── Ops that require attributes — Declined ──────────────────── "Squeeze" @@ -412,6 +449,14 @@ impl ShapeInference { | "InstanceNormalization" | "GroupNormalization" | "LpNormalization" + // Shape-preserving attention / MoE / KV-cache ops. See the note on + // the same list in `for_op`: absent from this table they resolve to + // `Declined` and `GetCapability` hands them to ORT's CPU EP. + | "MoE" + | "QMoE" + | "ScatterND" + | "ScatterElements" + | "Trilu" | "Clip" => Self::SameAsInput(0), // ── LayerNorm / SkipLayerNorm family ────────────────────────── @@ -575,6 +620,28 @@ impl ShapeInference { } } "RotaryEmbedding" => Self::RotaryEmbedding, + "PackedMultiHeadAttention" => Self::PackedMultiHeadAttention, + + // `com.microsoft::Attention` — packed QKV, guarded on domain so the + // opset-23 `ai.onnx::Attention` arm below still owns its own + // signature. The output width is `v_hidden`, which the attribute + // gives directly when present; otherwise it is `weights.dim1 / 3`, + // resolved from the input shapes at Compute time because this + // entry point does not see them. + "Attention" if domain == "com.microsoft" => { + let num_heads = int_attr("num_heads").unwrap_or(0).max(0) as usize; + let v_hidden = ints_attr("qkv_hidden_sizes").and_then(|sizes| { + // Malformed unless all three are present and positive; fall + // back to the weight-derived split rather than trusting a + // partial attribute. + (sizes.len() == 3 && sizes.iter().all(|s| *s > 0)).then(|| sizes[2] as usize) + }); + Self::MsftAttention { + v_hidden, + num_heads, + num_outputs, + } + } // ── Standard attention (ai.onnx::Attention, opset 23+) ───────── "Attention" if domain.is_empty() || domain == "ai.onnx" => { @@ -3323,6 +3390,96 @@ fn infer_shapes( Ok(vec![inputs[0].shape.to_vec()]) } + ShapeInference::MsftAttention { + v_hidden, + num_heads, + num_outputs, + } => { + // input(0): [B, S, input_hidden] (unprojected activation) + // input(1): [input_hidden, q_hidden + k_hidden + v_hidden] + // output(0): [B, S, v_hidden] + // output(1): present [2, B, num_heads, P + S, head_size] + if inputs.len() < 2 { + return Err("Attention: expected at least input and weights".into()); + } + let x = inputs[0].shape; + if x.len() != 3 { + return Err(format!("Attention: input rank must be 3, got {}", x.len())); + } + let weights = inputs[1].shape; + if weights.len() != 2 { + return Err(format!( + "Attention: weights rank must be 2, got {}", + weights.len() + )); + } + // Without `qkv_hidden_sizes` the projection is split three ways, so + // an indivisible width is a malformed node rather than something to + // round. + let v = match v_hidden { + Some(v) => *v, + None => { + if !weights[1].is_multiple_of(3) { + return Err(format!( + "Attention: weights dim1 {} is not divisible by 3 and \ + qkv_hidden_sizes is absent", + weights[1] + )); + } + weights[1] / 3 + } + }; + let mut outputs = vec![vec![x[0], x[1], v]]; + + if *num_outputs > 1 { + if *num_heads == 0 { + return Err( + "Attention: num_heads is required to shape the present output".into(), + ); + } + if !v.is_multiple_of(*num_heads) { + return Err(format!( + "Attention: v_hidden {v} is not divisible by num_heads {num_heads}" + )); + } + let head_size = v / num_heads; + // `past` is input(4): [2, B, num_heads, past_seq, head_size]. + let past_seq = inputs + .get(4) + .map(|p| if p.shape.len() >= 5 { p.shape[3] } else { 0 }) + .unwrap_or(0); + let present = vec![2, x[0], *num_heads, past_seq + x[1], head_size]; + for _ in 1..*num_outputs { + outputs.push(present.clone()); + } + } + Ok(outputs) + } + + ShapeInference::PackedMultiHeadAttention => { + // Tokens are packed with padding removed, so the output is rank-2: + // [token_count, v_hidden] = [query.dim0, value.dim1]. Neither dim + // can be read off input[0] alone, which is why this is not + // `SameAsInput(0)`. + if inputs.len() < 3 { + return Err( + "PackedMultiHeadAttention: expected at least query, key and value".into(), + ); + } + let q = inputs[0].shape; + let v = inputs[2].shape; + if q.is_empty() { + return Err("PackedMultiHeadAttention: query must not be rank-0".into()); + } + if v.len() < 2 { + return Err(format!( + "PackedMultiHeadAttention: value rank must be >= 2, got {}", + v.len() + )); + } + Ok(vec![vec![q[0], v[1]]]) + } + ShapeInference::AttentionStd { q_num_heads, kv_num_heads, @@ -3804,20 +3961,25 @@ mod tests { } #[test] - fn attention_std_for_node_reads_heads_and_declines_other_domains() { + fn attention_for_node_routes_each_domain_to_its_own_rule() { use onnx_runtime_ir::{Node, NodeId}; - // Default-domain Attention is modelled. + // Default-domain Attention is the opset-23 signature. let node = Node::new(NodeId(0), "Attention", Vec::new(), Vec::new()); assert!(matches!( ShapeInference::for_node(&node, &[], 1), ShapeInference::AttentionStd { .. } )); - // A foreign domain is not claimed by this arm. + // `com.microsoft::Attention` is a different operator -- its input is + // unprojected and the fused weight sets the output width -- so it must + // not fall into the opset-23 arm. It used to be `Declined` instead, + // which silently handed every node to ORT's CPU EP; it now has its own + // rule, and `plugin_ort_e2e`'s `msft_attention_assignment_f32` fixture + // is the end-to-end falsifier for that. let mut contrib = Node::new(NodeId(0), "Attention", Vec::new(), Vec::new()); contrib.domain = "com.microsoft".into(); assert!(matches!( ShapeInference::for_node(&contrib, &[], 1), - ShapeInference::Declined { .. } + ShapeInference::MsftAttention { .. } )); } diff --git a/docs/benchmarks/2026-08-15-cpu-ep-vs-ort-attention-moe.md b/docs/benchmarks/2026-08-15-cpu-ep-vs-ort-attention-moe.md index 84cde6b3de..8e31e90884 100644 --- a/docs/benchmarks/2026-08-15-cpu-ep-vs-ort-attention-moe.md +++ b/docs/benchmarks/2026-08-15-cpu-ep-vs-ort-attention-moe.md @@ -581,8 +581,15 @@ are load-bearing. Within *this* session MoE is at parity at t=1 and loses ## 10. Assignment matrix after Phase 3 -What the **plugin** EP asks ORT to hand over. This does not affect a native -`InferenceSession`, which has no host to defer to and runs every kernel. +> **WITHDRAWN — see §23.** Every `defer` row in this table is void. The project +> rule is now that a node this EP can execute is never handed to ORT's CPU EP, +> whatever the measurement says, and a losing row is a kernel to fix rather than +> a node to give away. The table is kept because the *ratios* are real evidence +> and they are the work queue; the `claim` column is not the current behaviour. + +What the **plugin** EP asked ORT to hand over, as of Phase 3. This never +affected a native `InferenceSession`, which has no host to defer to and runs +every kernel. | op | dtype | claim | evidence | |---|---|---|---| @@ -924,7 +931,7 @@ Full matrix, all 54 cells, `native ÷ ORT` (lower is better, 1.00 = parity): | qwen3-0.6b | prefill q512/kv512 | nomask | 1 | 1.063 | 1.076 | **0.99x** | | qwen3-0.6b | prefill q512/kv512 | nomask | 8 | 2.929 | 2.840 | **1.03x** | | qwen3-0.6b | prefill q512/kv512 | nomask | 16 | 2.876 | 3.149 | **0.91x** | -### Second answer: still decline it +### Second answer: still decline it — WITHDRAWN, see §23 The fix is large — up to **8.44x** — and masked single-thread decode now beats ORT outright (0.889–1.009x, was 1.073–1.400x). It is not enough. **44 of 54 @@ -933,10 +940,13 @@ cells are still slower than ORT**, 41 of them by ≥5%, and only 7 are ≥5% fas 16 threads**, because the remaining time is in the QK and PV GEMMs and their thread scaling, not in the scale/mask/softmax stage that #1094 fixed. -So the honest answer to requirement 4 is the one it anticipated: **the fused -region does not clear the ≥5% bar, and should be declined to ORT wherever the -Plugin EP has that option.** Keeping `Softmax` claimed *purely* to preserve a -fusion that then loses is not justified by this data. +So the honest answer to requirement 4, *at the time*, was the one it +anticipated: the fused region did not clear the ≥5% bar. The conclusion drawn +from that — "decline it to ORT" — **is withdrawn (§23)**; the measurement is +not. What the data actually establishes is that **the fused region is 3–15x +short and that is the work**: Keeping `Softmax` claimed purely to preserve a fusion +that then loses is not *justified* by this data — but it is now *required* by +the rule in §23, so the only acceptable resolution is to close the gap. The blocker is structural rather than a matter of will: `claim_preference(op, opset, shapes, input_dtypes)` in `assignment_policy.rs` is **per-node and has no @@ -1646,3 +1656,273 @@ this measurement's 1 us display resolution and well inside the row's own ~4% spread. The evidence that the read is now cached is the code, not this table. What these rows do show is that the reworked gate ordering did not disturb the below-threshold path, which is what they are here for. + +## 23. The rule changed: this EP never hands a node to ORT's CPU EP + +Phases 1–4 treated "decline to ORT" as a legitimate outcome for a range where we +lose, and §10 and §15 above are written that way. That is no longer the project +rule: + +> 我们的 cpu ep 不要分配到 ort cpu ep 上。用了我们的 cpu ep 就不要用 ort 的 cpu ep。但凡比 ort 慢的都要想方设法比 ort 快。 +> +> *(Our CPU EP must not assign work onto ORT's CPU EP. If you are using our CPU +> EP, do not use ORT's. Anything slower than ORT must be made faster than ORT, +> by whatever means it takes.)* + +So a losing cell is a kernel to fix, not a node to give away, and every ratio in +this document is now a work item rather than a justification. Nothing about the +*measurements* changes — they were paired, interleaved, same-host and +same-thread, and they still say what they said. + +### 23.1 The code-level declines were already gone; the silent one was not + +The perf-based decline was withdrawn before this section was written: +`claim_preference_node` returns `Claim` unconditionally and `assignment_policy.rs` +is deleted. Auditing what that actually guaranteed turned up two holes. + +**Hole 1 — the guarantee was untested for the operators this EP exists for.** +`plugin_ort_e2e`'s `ASSIGNMENT_FIXTURES` covered 23 activation and normalisation +graphs and *zero* attention, MoE, KV-cache, Softmax, Transpose or RoPE graphs. +The rule was asserted where it was easy, not where it mattered. + +**Hole 2 — `GetCapability`'s shape filter is a third, silent decline layer.** +Three independent gates must all pass for a node to reach our kernels: +`supports_op` (dtype/shape capability), `claim_preference_node` (was perf), and a +**fail-closed shape-inference filter** that drops any claim whose node has no +rule in `compute.rs`. That last one answers to neither of the first two, and it +was giving away `com.microsoft::Attention`, `MoE`, `QMoE`, +`PackedMultiHeadAttention`, `ScatterND`, `ScatterElements` and `Trilu`. For +`PackedMultiHeadAttention`, which **ORT has no CPU kernel for at all**, that +"fallback" bought a load failure rather than a slower run; for the rest it +simply meant not running an op we implement. + +### 23.2 Two decode ops were being handed to ORT by a dtype rule + +With shape rules added for all seven ops and fourteen new fixtures wired in, a real +ORT 1.27 session immediately failed the rewritten assignment test on the two ops +that matter most in decode: + +``` +2 of 32 fixtures were handed to ORT's CPU EP. + rotary_assignment_f32: 'RotaryEmbedding' — ours=[], ORT was given ["RotaryEmbedding"] + gqa_assignment_f32: 'GroupQueryAttention' — ours=[], ORT was given ["GroupQueryAttention"] +``` + +The cause is that the plugin advertises one *union* of dtypes per op and tests +every input slot against it. Attention ops map to `FLOAT_DTYPES`, but +`RotaryEmbedding`'s `position_ids` is int64 and `GroupQueryAttention`'s +`seqlens_k` / `total_sequence_length` are int32, so the integer slots failed the +float test and the claim was dropped. `input_dtype_constraints_for_op` already +existed for the opposite problem (a union too *wide* for `MatMulNBits`); it just +had no entries for the attention family. Adding the per-slot tables — with the +`com.microsoft` and `ai.onnx` RoPE slot orders kept separate, because they differ +— is what makes these two ops reachable at all. + +**This means §1's and #1078's RoPE and GQA plugin-path numbers describe an EP +that was not running those ops.** Any plugin-path attention measurement taken +before this fix needs re-taking. + +### 23.3 ORT stamps schema defaults on the node, and one of them is not 0 + +With the dtype fix in, GQA still failed — at kernel construction: + +``` +STAGE [CreateSession] FAILED: get_kernel failed for node '' (GroupQueryAttention): + GroupQueryAttention: smooth_softmax is not yet supported (got -1) +``` + +The fixture never sets `smooth_softmax`. ORT materialises **schema defaults** +onto a node before an EP sees it, and the contrib schema's default for that +attribute is **-1**, not 0. ORT's own kernel enables the feature only for the +exact value 1, so -1 means "off" — but our `!= 0` test read it as "on" and +refused the node. Every GQA node from every real ORT session hit this. + +**Correction, from review:** the same argument was originally made for `scale`, +and it is wrong. Instrumenting the GQA, MHA and `com.microsoft::Attention` +factories during a real `CreateSession` over these fixtures prints +`scale_attr=None` for all three — `scale` is `OPTIONAL_VALUE` with no schema +default, so ORT does *not* stamp it, and removing the guard leaves the numerics +in §23.5 unchanged at 1.006e-7. The guard stays as **defence, not a fix**: both +ORT's kernels and ours read an explicit `scale = 0` as "use 1/sqrt(head_size)" +rather than literally, so a zero taken at face value would multiply every score +by zero and return a silently wrong answer instead of an error. `attention.rs`, +`msft_attention.rs`, `multi_head_attention.rs`, `group_query_attention.rs` and +`packed_multi_head_attention.rs` now all treat a non-positive `scale` as absent. +`> 0` is deliberately broader than ORT's `== 0`, because a negative scale is +meaningless and would otherwise be honoured. + +`smooth_softmax` is the load-bearing half of this finding and it does hold: +`smooth_softmax_attr=Some(-1)` on a node that never set it. + +### 23.4 Assignment matrix, all 32 fixtures, real ORT 1.27 CPU session + +`no_supported_node_is_ever_left_to_the_ort_cpu_ep` collects every failure and +asserts once, so a run reports the whole matrix instead of stopping at the first +decline. Every row is a real session with +`session.record_ep_graph_assignment_info=1`, queried through +`Session_GetEpGraphAssignmentInfo`. + +| fixture | op | before | after | +|---|---|---|---| +| `softmax_assignment_f32` | `Softmax` | no fixture | **ours** | +| `transpose_assignment_f32` | `Transpose` | no fixture | **ours** | +| `kv_concat_assignment_f32` | `Concat` | no fixture | **ours** | +| `kv_scatternd_assignment_f32` | `ScatterND` | silently declined | **ours** | +| `scatter_elements_assignment_f32` | `ScatterElements` | silently declined | **ours** | +| `trilu_assignment_f32` | `Trilu` | silently declined | **ours** | +| `rotary_assignment_f32` | `RotaryEmbedding` | **ORT** (dtype union) | **ours** | +| `mha_assignment_f32` | `MultiHeadAttention` | no fixture | **ours** | +| `gqa_assignment_f32` | `GroupQueryAttention` | **ORT** (dtype union) | **ours** | +| `gqa_rotary_pos_assignment_f32` | `GroupQueryAttention` + int64 `position_ids` | **ORT** (dtype union, slot 9) | **ours** | +| `msft_attention_assignment_f32` | `com.microsoft::Attention` | silently declined | **ours** | +| `packed_mha_assignment_f32` | `PackedMultiHeadAttention` | silently declined, then **ORT** (dtype union) | **ours** | +| `moe_assignment_f32` | `MoE` | silently declined | **ours** | +| `moe_assignment_f16` | `MoE` float16 | silently declined, then **ORT** (f32-only union) | **ours** | +| `qmoe_assignment_f32` | `QMoE` | silently declined, then **ORT** (dtype union) | **ours** | +| 23 pre-existing activation/norm fixtures | — | ours | **ours** | + +Six of those rows exist because review asked for them, and four of the six +found a decline that was still live after the first pass: GQA's `position_ids` +is optional input **9**, and listing only slots 5 and 6 left a `do_rotary` node +with explicit int64 positions failing the float union; `QMoE`'s uint8-packed +expert weights and `PackedMultiHeadAttention`'s int32 `token_offset` / +`cumulative_sequence_length` did the same. And `MoE` advertised **float32 +only** while its kernel widens float16 and bfloat16 to f32 and narrows on the +way out — so every *realistic* MoE node, which is half precision, was declined +while the f32 fixture passed. One dtype's worth of coverage is not coverage. +The pure-Rust inventory test cannot +see any of these — it builds synthetic nodes and never opens an ORT session, so +it is blind to the dtype filter and to the kernel factory. **Only a real session +per op finds them, which is the argument for one fixture per rescued op rather +than a representative sample.** + +`every_fixture_loads_with_cpu_fallback_disabled` runs the same 38 with +`session.disable_cpu_ep_fallback=1`, so ORT is *forbidden* from placing a +supported node on its own CPU EP: **38/38 load and 38/38 are assigned to us.** +**Correction, from review.** An earlier draft of this section claimed ORT has +no CPU kernel for `MoE`, `QMoE` or `com.microsoft::Attention`. It has all three; +the reviewer loaded *and ran* these very fixtures on ORT's +`CPUExecutionProvider` under both 1.27 and 1.28. Only +`PackedMultiHeadAttention` genuinely has none, which the falsifier shows +directly: reverting `PACKED_MHA_SLOTS` does not hand the node to ORT, it fails +session creation with `Could not find an implementation for +PackedMultiHeadAttention(1)`. For the other ops the decline meant we were not +running an operator we implement — bad, but not fatal. + +One fixture is deliberately **not** in that table: + +| fixture | op | on `main` | after | why excluded | +|---|---|---|---|---| +| `qmoe_columnwise_f32` | `QMoE`, `block_size` absent | ORT | ORT | no column-wise kernel here; see §23.6 | +| `moe_sparse_mixer_f32` | `MoE`, `use_sparse_mixer=1` | ORT | ORT | no sparse-mixer router here | +| `gqa_smooth_softmax_f32` | `GQA`, `smooth_softmax=1` | ORT | ORT | no attention-sink softmax here | + +`factory_only_capability_limits_are_declined_at_claim_time` asserts all three: +the node lands on ORT, the session still loads, and — the point of the test — +the decline happens in `supports_op` rather than in the kernel factory, where it +would have been unrecoverable. + +### 23.5 Assignment is not the guarantee — the arithmetic is + +A claimed node that computes the wrong answer is worse than the deferral it +replaced, so the two ops that were being handed away are also checked against +the implementation they displaced. `rope_and_gqa_execute_on_our_ep_and_match_ort_numerics` +runs each fixture twice over the same model file and the same input bytes: once +with our EP appended and fallback disabled, once with our EP simply not +appended, so ORT resolves the node to its own contrib CPU kernel. + +| fixture | output | values | max relative error vs ORT | +|---|---|---|---| +| `rotary_assignment_f32` | `output` | 4 096 | 1.182e-7 | +| `gqa_assignment_f32` | `output` | 4 096 | 1.006e-7 | +| `gqa_assignment_f32` | `present_key` | 1 048 576 | **0** | +| `gqa_assignment_f32` | `present_value` | 1 048 576 | **0** | + +Both attention outputs are at float32 rounding, and the KV cache is written +bit-identically to ORT's. + +### 23.6 Making an op reachable can break it: factory-only capability limits + +`QMoE`'s fixture had to be written with `block_size = 32`, because this kernel +implements only the blocked form while ORT's schema leaves `block_size` without +a default — so an absent attribute means the *column-wise* form, one scale per +output row. The first draft of this section assumed ORT could not run that +either. **It can**, and review demonstrated it by loading and running the +fixture on ORT's `CPUExecutionProvider` under 1.27 and 1.28. + +That turned a documented gap into a regression, and it is worth stating plainly +because it is a hazard of this whole change: + +> Claiming an op is not free. `GetCapability` consults only the dtype and shape +> filters; a rejection raised later, in the kernel factory, arrives **after** ORT +> has compiled the node onto this EP, and **no fallback recovers from it**. So +> making `QMoE` reachable also made a previously-working column-wise model die +> at `CreateSession` with `block_size must be a power of two and at least 16, +> got 0`. + +A third review round showed this was not one bug but a **class**, and that the +first fix had only patched one instance of it. Two more were live, both on +attributes real models set, and both of which ORT's CPU EP runs today: + +| op | attribute | who sets it | verified on ORT CPU | +|---|---|---|---| +| `QMoE` | `block_size` absent (column-wise) | ORT-quantized MoE exports | 1.27 and 1.28 | +| `MoE` / `QMoE` | `use_sparse_mixer=1` | Phi-3.5-MoE, GRIN-MoE | 1.28 | +| `GroupQueryAttention` | `smooth_softmax=1` | Gemma-style attention sink | 1.28 | + +The fix is structural rather than three more `if`s. Each kernel's attribute +parsing is now a single function, and the claim-time guard *is* that function: + +```rust +pub(crate) fn unsupported_reason(node: &Node) -> Option { + attributes_from_node(node).err().map(|e| e.to_string()) +} +``` + +so a limit cannot be added to one of these factories without appearing at claim +time too. Be precise about the scope of that guarantee: **within a wired op** +drift is impossible by construction; *wiring an op* is still discipline. +`supports_op` consults it for `MoE`, `QMoE`, `GroupQueryAttention`, +`MultiHeadAttention`, `com.microsoft::Attention`, `ai.onnx::Attention` and +`PackedMultiHeadAttention`, which sweeps up the rest of the family in one go: +GQA's quantized-KV and `qk_output` rejections, msft `Attention`'s `do_rotary` +and `past_present_share_buffer`, `ai.onnx::Attention`'s `qk_matmul_output_mode`, +and `QMoE`'s `expert_weight_bits` / `quant_type`. + +Two tests pin it. `provider::tests::every_factory_attribute_rejection_is_mirrored_at_claim_time` +is pure Rust — for eleven hostile nodes it asserts *both* that the factory +rejects and that `supports_op` declines, so it fails if the two ever diverge — +and `plugin_ort_e2e::factory_only_capability_limits_are_declined_at_claim_time` +proves it against real ORT for the three fixtures above: the node lands on ORT, +the session loads. Disabling the claim-time guard turns each of the three into + +``` +STAGE [CreateSession] FAILED: Compile: get_kernel failed for node '' (MoE): + MoE: use_sparse_mixer=1 is unsupported by the Phase-1 CPU reference kernel +``` + +Review then audited the rest of the EP for the same defect and found ten other +factories that reject something `supports_op` does not pre-check — `MatMulNBits` +(`bits`, `block_size`), `Resize`, `Pad`, `GridSample`, `LpNormalization`, +`Unique`, `BitShift`, `ConstantOfShape` and two `pkg.nxrt` internals. **None is +a live regression**: each rejects only schema-invalid values or configurations +ORT's own CPU kernel also refuses, and the reviewer could not construct a case +ORT runs and we reject. They are recorded here so the next person does not have +to redo the audit, and so the claim above is not read as EP-wide. + +**These are the only deliberate declines in the suite and every one is a +capability answer, not a performance one** — we have no column-wise, sparse +mixer or smooth-softmax implementation, and the choice for each is between ORT +running the model and nobody running it. They are not exceptions to §23's rule, +which is about ranges where we are *slower*; they are an admission that we owe +three kernels. Each should stop being an exception as soon as its kernel +exists. + +### 23.7 What this does not fix + +It makes the losing ranges *reachable*; it does not make them fast. §15's fused +region is still 3–15x short at 8–16 threads, §19–20's Transpose gaps and §18's +MoE cells are unchanged, and #1078's own evidence has float32 RoPE losing 12/12 +cells at 1.53–17.21x. Under the rule above every one of those is now a kernel to +fix with no exit, and the RoPE grid is first because it is the op that was being +given away. diff --git a/docs/performance/CPU_ACTIVATION_GAPS.md b/docs/performance/CPU_ACTIVATION_GAPS.md index 68d917b006..8f0ea993c5 100644 --- a/docs/performance/CPU_ACTIVATION_GAPS.md +++ b/docs/performance/CPU_ACTIVATION_GAPS.md @@ -151,6 +151,64 @@ sets identical by construction, and The lesson is that an inventory test is only as good as its source of truth. Two rounds of review passed on tests that enumerated the wrong set. +**Filter 3, second failure mode — the dtype *union* is per-op, the kernel's rule +is per-slot.** An entry advertises one set of dtypes for the whole op and every +input slot is tested against it, so a mixed-dtype op is decided by whichever +constraint is written last. `MatMulNBits` showed the union being too *wide*. +The attention family shows it being too *narrow*, which is worse because it is +silent: the ops map to `FLOAT_DTYPES`, but `RotaryEmbedding`'s `position_ids` is +int64 and `GroupQueryAttention`'s `seqlens_k` / `total_sequence_length` are +int32, so the integer slots failed the float test and **both ops were handed to +ORT on every real session** while every pure-Rust test passed. +`input_dtype_constraints_for_op` (`onnx-runtime-ep-cpu/src/kernels/mod.rs`) now +carries per-slot tables for `RotaryEmbedding` in both domains — their slot +orders differ — plus `GroupQueryAttention` (including `position_ids` at slot +**9**, which a first pass missed because it only listed the two slots the +fixtures happened to exercise), `MultiHeadAttention`, `com.microsoft::Attention`, +`PackedMultiHeadAttention` (int32 `token_offset` / `cumulative_sequence_length`) +and `QMoE` (uint8-packed experts and zero points). + +The union can also be too narrow without any mixed-dtype slot at all: `MoE` +advertised **float32 only** while its kernel accepts float16 and bfloat16 too, +so the f32 fixture passed and every production half-precision mixture was +declined. A single dtype's worth of coverage is not coverage. + +Four of those six were found only after review asked for one real-ORT fixture +per rescued op. **An inventory test that never opens a session cannot see this +filter at all**, which is the same lesson as above arriving a third time. + +**Filter 4 — the kernel factory, which runs after assignment.** Clearing all +three filters only gets as far as `get_kernel`. ORT stamps **schema defaults** +onto a node before an EP sees it, so a factory that rejects an attribute it does +not support must use ORT's default, not ONNX's zero: the contrib default for +`smooth_softmax` is `-1`, and testing it for `!= 0` rejected every +`GroupQueryAttention` node ORT ever resolved. A rejection here is a hard session +failure rather than a fallback, so it is the most expensive of the four — and +that cuts both ways. Making an op *reachable* can break a model that used to +work: once `QMoE` was claimed, a column-wise (`block_size` absent) node that ORT +had been running fine reached our factory and killed `CreateSession`. Review +then found the same defect on `use_sparse_mixer=1` (Phi-3.5-MoE, GRIN-MoE) and +on `smooth_softmax=1` (Gemma-style attention sink) — both of which ORT's CPU EP +runs today — so it is a class, not three bugs. + +**Any capability limit a factory enforces must be mirrored in `supports_op`**, +where a decline is still recoverable. The mirror is now structural rather than +duplicated: each kernel's attribute validation lives in one function and the +claim-time guard is that same function's error, so a new factory limit cannot +fail to appear at claim time. `provider::tests::every_factory_attribute_rejection_is_mirrored_at_claim_time` +asserts both halves for eleven hostile nodes and fails if they diverge. The +guarantee is per-wired-op, though: within a wired op drift is impossible, but +wiring an op is still discipline. An audit of the remaining factories found ten +that reject something `supports_op` does not pre-check; none is a live +regression, because each refuses only schema-invalid values or configurations +ORT's own CPU kernel also refuses. + +The attention, MoE and KV-cache ops are now covered end-to-end by +`plugin_ort_e2e`'s `ASSIGNMENT_FIXTURES` (38 graphs, all on our EP with +`session.disable_cpu_ep_fallback=1`) and by +`rope_and_gqa_execute_on_our_ep_and_match_ort_numerics`, which checks the two +recovered ops against ORT's own kernels rather than only checking placement. + **64 registered ops remain in the shape-table gap.** The list is not prose: it is asserted exactly by `every_registered_op_has_a_shape_rule_or_is_a_known_gap` (`crates/onnx-runtime-ep-cpu-plugin/tests/shape_inference_coverage.rs`), which