55#include " disable_fp16_comp_flux2_rope.hpp"
66
77#include " openvino/core/graph_util.hpp"
8+ #include " openvino/op/add.hpp"
89#include " openvino/op/concat.hpp"
9- #include " openvino/op/cos.hpp"
10- #include " openvino/op/matmul.hpp"
11- #include " openvino/op/sin.hpp"
12- #include " openvino/op/transpose.hpp"
10+ #include " openvino/op/multiply.hpp"
11+ #include " openvino/op/reshape.hpp"
12+ #include " openvino/op/split.hpp"
13+ #include " openvino/op/squeeze.hpp"
14+ #include " openvino/op/unsqueeze.hpp"
1315#include " openvino/op/util/shape_of_base.hpp"
16+ #include " openvino/pass/pattern/op/optional.hpp"
1417#include " openvino/pass/pattern/op/wrap_type.hpp"
1518#include " transformations/rt_info/disable_precision_conversion.hpp"
1619#include " transformations/utils/utils.hpp"
@@ -23,9 +26,7 @@ namespace ov::intel_gpu {
2326
2427namespace {
2528
26- void mark_path_from_output (ov::Node* root,
27- std::unordered_set<ov::Node*>& visited,
28- const std::function<bool (ov::Node*)>& skip_node_predicate) {
29+ void mark_path_from_output (ov::Node* root, std::unordered_set<ov::Node*>& visited, const std::function<bool (ov::Node*)>& skip_node_predicate) {
2930 if (!root || visited.count (root))
3031 return ;
3132 auto visit_func = [](ov::Node* node) {
@@ -36,8 +37,7 @@ void mark_path_from_output(ov::Node* root,
3637
3738auto skip_node_predicate () {
3839 return [](ov::Node* node) -> bool {
39- return ov::is_type<v0::Constant>(node) || ov::is_type<v0::Parameter>(node) ||
40- ov::is_type<op_util::ShapeOfBase>(node);
40+ return ov::is_type<v0::Constant>(node) || ov::is_type<v0::Parameter>(node) || ov::is_type<op_util::ShapeOfBase>(node);
4141 };
4242}
4343
@@ -46,40 +46,53 @@ auto skip_node_predicate() {
4646DisableFP16CompFlux2RoPEPattern::DisableFP16CompFlux2RoPEPattern () {
4747 using namespace ov ::pass::pattern;
4848
49- // FLUX.2 pos_embed frequency tables: outer-product MatMul -> Cos / Sin.
50- auto outer_matmul = wrap_type<v0::MatMul>({any_input (), any_input ()});
51- auto pos_cos = wrap_type<v0::Cos>({outer_matmul});
52- auto pos_sin = wrap_type<v0::Sin>({outer_matmul});
53-
54- // rotary_emb / LLM-style tables: Concat -> Cos or Sin (e.g. text encoder rotary_emb).
55- auto rot_concat = wrap_type<v0::Concat>({any_input (), any_input ()});
56- auto table_cos = wrap_type<v0::Cos>({rot_concat});
57- auto table_sin = wrap_type<v0::Sin>({rot_concat});
58-
59- // MatMul -> Transpose -> Concat -> Cos/Sin (MarkRopeInputsToKeepInMixedPrecision reference pattern).
60- auto table_matmul = wrap_type<v0::MatMul>({any_input (), any_input ()});
61- auto table_transpose = wrap_type<v1::Transpose>({table_matmul, any_input ()});
62- auto table_concat = wrap_type<v0::Concat>({table_transpose, table_transpose}, {{" axis" , -1 }});
63- auto ref_cos = wrap_type<v0::Cos>({table_concat});
64- auto ref_sin = wrap_type<v0::Sin>({table_concat});
65-
66- auto register_mark_root = [this ](const std::shared_ptr<ov::Node>& pattern_root, const char * name) {
67- ov::matcher_pass_callback callback = [OV_CAPTURE_CPY_AND_THIS ](Matcher& m) {
68- if (transformation_callback (m.get_match_root ()))
69- return false ;
70- const auto skip_pred = skip_node_predicate ();
71- mark_path_from_output (m.get_pattern_value_map ().at (pattern_root).get_node (), m_visited, skip_pred);
72- return true ;
73- };
74- register_matcher (std::make_shared<Matcher>(pattern_root, name), callback);
49+ // This pass runs *before* RoPEFusion, so the RoPE is still decomposed and the
50+ // fused ov::op::internal::RoPE op does not exist yet. We therefore anchor on the
51+ // decomposed application graph, mirroring ov::pass::RoPEFusionFlux but without the
52+ // symbolic shape constraints (there is no symbolic inference context here):
53+ //
54+ // x1 = Reshape(x)
55+ // x1_0,x1_1 = Split(x1, axis=-1, num_splits=2)
56+ // x2 = Concat(-x1_1, x1_0, axis=-1) // rotate_half(x)
57+ // x3 = Reshape(x2)
58+ // y = x * t_cos + x3 * t_sin // <-- match root
59+ //
60+ // Only t_cos / t_sin captured from this structure are genuine rotary tables, so we
61+ // walk backward from exactly those two tensors. This avoids pulling unrelated
62+ // MatMul->Cos / Concat->Cos subgraphs into FP32.
63+ auto x = any_input ();
64+ auto t_cos = any_input ();
65+ auto t_sin = any_input ();
66+
67+ auto x1 = wrap_type<v1::Reshape>({x, any_input ()});
68+ auto split = wrap_type<v1::Split>({x1, -1 }, {{" num_splits" , 2 }});
69+ split->set_output_size (2 );
70+
71+ // The negation of one split half is optionally wrapped in Squeeze/Unsqueeze
72+ // depending on which transformations ran before this pass (see RoPEFusionFlux).
73+ auto opt_squeeze = optional<v0::Squeeze>({split->output (1 ), -1 });
74+ auto x1_1_neg = wrap_type<v1::Multiply>({opt_squeeze, -1 }, {{" auto_broadcast" , " numpy" }});
75+ auto opt_squeeze_1 = optional<v0::Squeeze>({x1_1_neg, -1 });
76+ auto opt_unsqueeze = optional<v0::Unsqueeze>({opt_squeeze_1, -1 });
77+
78+ auto x2 = wrap_type<v0::Concat>({opt_unsqueeze, split->output (0 )}, {{" axis" , -1 }});
79+ auto x3 = wrap_type<v1::Reshape>({x2, any_input ()});
80+
81+ auto y1 = wrap_type<v1::Multiply>({x, t_cos}, {{" auto_broadcast" , " numpy" }});
82+ auto y2 = wrap_type<v1::Multiply>({x3, t_sin}, {{" auto_broadcast" , " numpy" }});
83+ auto result = wrap_type<v1::Add>({y1, y2}, {{" auto_broadcast" , " numpy" }});
84+
85+ ov::matcher_pass_callback callback = [OV_CAPTURE_CPY_AND_THIS ](Matcher& m) {
86+ if (transformation_callback (m.get_match_root ()))
87+ return false ;
88+ const auto & pattern_map = m.get_pattern_value_map ();
89+ const auto skip_pred = skip_node_predicate ();
90+ mark_path_from_output (pattern_map.at (t_cos).get_node (), m_visited, skip_pred);
91+ mark_path_from_output (pattern_map.at (t_sin).get_node (), m_visited, skip_pred);
92+ return false ;
7593 };
7694
77- register_mark_root (pos_cos, " DisableFP16CompFlux2RoPEPattern_PosCos" );
78- register_mark_root (pos_sin, " DisableFP16CompFlux2RoPEPattern_PosSin" );
79- register_mark_root (table_cos, " DisableFP16CompFlux2RoPEPattern_TableCos" );
80- register_mark_root (table_sin, " DisableFP16CompFlux2RoPEPattern_TableSin" );
81- register_mark_root (ref_cos, " DisableFP16CompFlux2RoPEPattern_RefCos" );
82- register_mark_root (ref_sin, " DisableFP16CompFlux2RoPEPattern_RefSin" );
95+ register_matcher (std::make_shared<Matcher>(result, " DisableFP16CompFlux2RoPEPattern" ), callback);
8396}
8497
8598} // namespace ov::intel_gpu
0 commit comments