-
Notifications
You must be signed in to change notification settings - Fork 146
[AIMIGRAPHX-1215] add runtime symbol resolution op #5085
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
82f335f
30b3e07
7c31007
6b9fc74
a0803a1
1416391
6e490a9
f6f858b
4c03db9
3f80d98
20a1f49
aff1a33
3c3908c
0910e3e
8e44892
2ee291f
195aa1c
a2a12d2
d199576
f6f15bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -220,6 +220,7 @@ register_migraphx_ops( | |
| elu | ||
| equal | ||
| erf | ||
| eval_expr_from_shape | ||
| exp | ||
| fill | ||
| fixed_pad | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| /* | ||
| * The MIT License (MIT) | ||
| * | ||
| * Copyright (c) 2015-2026 Advanced Micro Devices, Inc. All rights reserved. | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| * of this software and associated documentation files (the "Software"), to deal | ||
| * in the Software without restriction, including without limitation the rights | ||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| * copies of the Software, and to permit persons to whom the Software is | ||
| * furnished to do so, subject to the following conditions: | ||
| * | ||
| * The above copyright notice and this permission notice shall be included in | ||
| * all copies or substantial portions of the Software. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| * THE SOFTWARE. | ||
| */ | ||
| #ifndef MIGRAPHX_GUARD_OPERATORS_EVAL_EXPR_FROM_SHAPE_HPP | ||
| #define MIGRAPHX_GUARD_OPERATORS_EVAL_EXPR_FROM_SHAPE_HPP | ||
|
|
||
| #include <migraphx/argument.hpp> | ||
| #include <migraphx/check_shapes.hpp> | ||
| #include <migraphx/config.hpp> | ||
| #include <migraphx/context.hpp> | ||
| #include <migraphx/shape.hpp> | ||
| #include <migraphx/sym.hpp> | ||
| #include <migraphx/zip_view.hpp> | ||
| #include <algorithm> | ||
| #include <unordered_map> | ||
| #include <unordered_set> | ||
| #include <vector> | ||
|
|
||
| namespace migraphx { | ||
| inline namespace MIGRAPHX_INLINE_NS { | ||
| namespace op { | ||
|
|
||
| struct eval_expr_from_shape | ||
| { | ||
| std::vector<sym::expr> expressions{}; | ||
| std::vector<shape> input_shapes{}; | ||
|
|
||
| template <class Self, class F> | ||
| static auto reflect(Self& self, F f) | ||
| { | ||
| return pack(f(self.expressions, "expressions"), f(self.input_shapes, "input_shapes")); | ||
| } | ||
|
|
||
| std::string name() const { return "eval_expr_from_shape"; } | ||
|
|
||
| shape compute_shape(const std::vector<shape>& inputs) const | ||
| { | ||
| check_shapes{inputs, *this, true}.has_at_least(1); | ||
|
|
||
| std::unordered_set<sym::expr> missing; | ||
| for(const auto& expression : expressions) | ||
| { | ||
| auto variables = sym::find_variables(expression); | ||
| missing.merge(variables); | ||
| } | ||
|
|
||
| for(const auto& input : inputs) | ||
| { | ||
| if(not input.symbolic()) | ||
| continue; | ||
| for(const auto& d : input.dyn_dims()) | ||
| if(d.sym_expr.name() == "variable") | ||
| missing.erase(sym::as_symbol(d.sym_expr)); | ||
| } | ||
| if(not missing.empty()) | ||
| MIGRAPHX_THROW("EVAL_EXPR_FROM_SHAPE: Symbol '" + missing.begin()->to_string() + | ||
| "' is not a direct input dimension"); | ||
|
|
||
| return shape{shape::int64_type, {expressions.size()}}; | ||
| } | ||
|
|
||
| void finalize(context&, const shape&, const std::vector<shape>& inputs) | ||
| { | ||
| input_shapes = inputs; | ||
| } | ||
|
|
||
| argument compute(const shape&, std::vector<argument> args) const | ||
| { | ||
| if(input_shapes.empty() or input_shapes.size() != args.size()) | ||
| MIGRAPHX_THROW("EVAL_EXPR_FROM_SHAPE: input shapes not captured; op was not finalized"); | ||
|
|
||
| std::unordered_map<sym::expr, std::size_t> values; | ||
| for(auto&& [input_shape, arg] : views::zip(input_shapes, args)) | ||
| { | ||
| const auto& lens = arg.get_shape().lens(); | ||
| if(input_shape.ndim() != lens.size()) | ||
| MIGRAPHX_THROW("EVAL_EXPR_FROM_SHAPE: Runtime input rank does not match its " | ||
| "symbolic shape"); | ||
| if(not input_shape.symbolic()) | ||
| continue; | ||
| const auto& dims = input_shape.dyn_dims(); | ||
| for(auto&& [dim, len] : views::zip(dims, lens)) | ||
| { | ||
| if(dim.sym_expr.name() != "variable") | ||
| continue; | ||
| auto variable = sym::as_symbol(dim.sym_expr); | ||
| auto result = values.emplace(variable, len); | ||
| if(not result.second and result.first->second != len) | ||
| MIGRAPHX_THROW("EVAL_EXPR_FROM_SHAPE: Repeated symbol has inconsistent runtime " | ||
| "dimensions"); | ||
| } | ||
| } | ||
|
|
||
| argument result{shape{shape::int64_type, {expressions.size()}}}; | ||
| result.visit([&](auto output) { | ||
| std::transform(expressions.begin(), | ||
| expressions.end(), | ||
| output.begin(), | ||
| [&](const auto& e) { return e.eval_uint(values); }); | ||
| }); | ||
| return result; | ||
| } | ||
| }; | ||
|
|
||
| } // namespace op | ||
| } // namespace MIGRAPHX_INLINE_NS | ||
| } // namespace migraphx | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,39 @@ | |
| #include "test.hpp" | ||
| #include "rob.hpp" | ||
|
|
||
| struct can_eval_finalize_passthrough | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reason for this logic?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just a test to make sure const-folding doesnt try and fold an on op that requires finalization. This is just a dummy op testing that logic. |
||
| { | ||
| std::string name() const { return "can_eval_finalize_passthrough"; } | ||
|
|
||
| migraphx::shape compute_shape(const std::vector<migraphx::shape>& inputs) const | ||
| { | ||
| return inputs.at(0); | ||
| } | ||
|
|
||
| migraphx::argument compute(const migraphx::shape&, | ||
| const std::vector<migraphx::argument>& args) const | ||
| { | ||
| return args.at(0); | ||
| } | ||
|
|
||
| void finalize(migraphx::context&, const migraphx::shape&, const std::vector<migraphx::shape>&) | ||
| { | ||
| } | ||
| }; | ||
|
|
||
| TEST_CASE(can_eval_rejects_finalize_op) | ||
| { | ||
| migraphx::module m; | ||
| auto one = m.add_literal(1); | ||
| auto evaluable = m.add_instruction(migraphx::make_op("identity"), one); | ||
| auto finalized = m.add_instruction(can_eval_finalize_passthrough{}, one); | ||
| auto dependent = m.add_instruction(migraphx::make_op("identity"), finalized); | ||
|
|
||
| EXPECT(evaluable->can_eval()); | ||
| EXPECT(not finalized->can_eval()); | ||
| EXPECT(not dependent->can_eval()); | ||
| } | ||
|
|
||
| TEST_CASE(check_undefined) | ||
| { | ||
| migraphx::module m; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like it should be
dim.sym_expr.name() == "literal"as we can replace in arbitrary expression.