6161#include " koladata/expr/expr_operators.h"
6262#include " koladata/expr/non_determinism.h"
6363#include " koladata/functor/auto_variables.h"
64+ #include " koladata/functor/call.h"
6465#include " koladata/functor/functor.h"
65- #include " koladata/functor/parallel/transform_config.pb.h"
6666#include " koladata/functor/parallel/transform_config.h"
67+ #include " koladata/functor/parallel/transform_config.pb.h"
6768#include " koladata/functor/signature_utils.h"
6869#include " koladata/functor_storage.h"
6970#include " koladata/internal/data_item.h"
7071#include " koladata/internal/dtype.h"
7172#include " koladata/object_factories.h"
73+ #include " koladata/operators/core.h"
7274#include " koladata/operators/slices.h"
7375#include " koladata/signature.h"
7476#include " koladata/signature_storage.h"
@@ -278,24 +280,15 @@ class InnerTransformManager {
278280 }
279281 used_var_names_.insert (result.var_name );
280282 ASSIGN_OR_RETURN (DataSlice var, functor_.GetAttr (var_name));
281- if (!var.item ().holds_value <arolla::expr::ExprQuote>()) {
282- ASSIGN_OR_RETURN (
283- auto transformed_var,
284- TransformEager (arolla::TypedValue::FromValue (std::move (var))));
285- ASSIGN_OR_RETURN (auto transformed_var_slice,
286- std::move (transformed_var).As <DataSlice>());
287- new_vars_.emplace_back (result.var_name , std::move (transformed_var_slice));
288- results_.emplace (var_name, result);
289- return result;
283+ std::optional<arolla::TypedValue> var_qvalue;
284+ if (var.item ().holds_value <arolla::expr::ExprQuote>()) {
285+ ASSIGN_OR_RETURN (var_qvalue, TryComputeLiteral (var));
286+ } else {
287+ var_qvalue = arolla::TypedValue::FromValue (std::move (var));
290288 }
291- ASSIGN_OR_RETURN (auto var_expr,
292- var.item ().value <arolla::expr::ExprQuote>().expr ());
293- if (expr::IsLiteral (var_expr)) {
294- if (!var_expr->qvalue ()) {
295- return absl::InternalError (" Literal does not have a value" );
296- }
289+ if (var_qvalue.has_value ()) {
297290 ASSIGN_OR_RETURN (auto transformed_var,
298- TransformEager (*var_expr-> qvalue ( )));
291+ TransformEager (*std::move (var_qvalue )));
299292 auto new_var_expr = arolla::expr::Literal (std::move (transformed_var));
300293 auto new_var = DataSlice::CreatePrimitive (
301294 arolla::expr::ExprQuote{std::move (new_var_expr)});
@@ -305,18 +298,17 @@ class InnerTransformManager {
305298 }
306299 if (!config_->allow_runtime_transforms ()) {
307300 return absl::InvalidArgumentError (absl::StrCat (
308- " The parallel transformation requires that all sub-functors being"
309- " called are specified as literals, instead of being computed"
310- " dynamically, so that we can transform them recursively. In case you"
311- " are calling a sub-functor that is computed dynamically, but do not"
312- " need to recursively transform it (evaluating this sub-functor"
313- " single-threaded is fine), you can use"
314- " kd.functor.call_fn_normally_when_parallel and its variants to call"
315- " the sub-functor. In case you do need to evaluate a dynamically"
316- " computed sub-functor in a parallel fashion, you can pass"
317- " allow_runtime_transforms=True to kd.parallel.transform, but this"
318- " will be slower and should not be used in production."
319- " \n The offending sub-functor " ,
301+ " The parallel transformation requires that all sub-functors being "
302+ " called are fully defined at transformation time, so that we can "
303+ " transform them recursively. In case you are calling a sub-functor "
304+ " that is computed dynamically, but do not need to recursively "
305+ " transform it (evaluating this sub-functor single-threaded is fine), "
306+ " you can use kd.functor.call_fn_normally_when_parallel and its "
307+ " variants to call the sub-functor. In case you do need to evaluate a "
308+ " dynamically computed sub-functor in a parallel fashion, you can "
309+ " pass allow_runtime_transforms=True to kd.parallel.transform, but "
310+ " this will be slower and should not be used in production.\n The "
311+ " offending sub-functor " ,
320312 var_name, " : " , arolla::Repr (var),
321313 " . The whole functor: " , arolla::Repr (functor_)));
322314 }
@@ -355,6 +347,38 @@ class InnerTransformManager {
355347 std::move (sub_functor)});
356348 }
357349
350+ // If expr can be literal folded (with respect of its dependencies in
351+ // functor_), returns its value. Otherwise returns std::nullopt.
352+ absl::StatusOr<std::optional<arolla::TypedValue>> TryComputeLiteral (
353+ const DataSlice& expr) const {
354+ // TODO: b/477578091 - we are changing the functor in order to reuse
355+ // CallFunctorWithCompilationCache. It will be more efficient if we just
356+ // evaluate all the variables needed for `expr` explicitly.
357+ ASSIGN_OR_RETURN (
358+ DataBagPtr returns_db,
359+ ops::Attr (functor_,
360+ DataSlice::CreatePrimitive (arolla::Text (kReturnsAttrName )),
361+ expr,
362+ /* override_schema=*/ DataSlice::CreatePrimitive (false )));
363+ ASSIGN_OR_RETURN (
364+ DataBagPtr signature_db,
365+ ops::Attr (functor_,
366+ DataSlice::CreatePrimitive (arolla::Text (kSignatureAttrName )),
367+ KodaEmptySignature (),
368+ /* override_schema=*/ DataSlice::CreatePrimitive (false )));
369+ ASSIGN_OR_RETURN (
370+ auto db, DataBag::ImmutableEmptyWithFallbacks ({std::move (returns_db),
371+ std::move (signature_db),
372+ functor_.GetBag ()}));
373+ auto new_functor = functor_.WithBag (std::move (db));
374+ ASSIGN_OR_RETURN (
375+ auto res, functor::CallFunctorWithCompilationCache (new_functor, {}, {}),
376+ // We consider all functor evaluation errors as caused by being not
377+ // actually a literal.
378+ std::nullopt );
379+ return res;
380+ }
381+
358382 const ParallelTransformConfigPtr absl_nonnull& config_;
359383 const DataSlice& functor_;
360384 const expr::InputContainer& variable_container_;
0 commit comments