From 16a5a18b0aec2acbfe36e1a4c7be7e43341f900b Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Fri, 14 Feb 2025 03:18:13 +0530 Subject: [PATCH 01/11] Added an if condition to set trust_input to True if argument list is empty in __call__ function of Function class in pytensor/compile/function/types.py --- pytensor/compile/function/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytensor/compile/function/types.py b/pytensor/compile/function/types.py index 0ccaa9e00b..33642b7c2b 100644 --- a/pytensor/compile/function/types.py +++ b/pytensor/compile/function/types.py @@ -916,6 +916,8 @@ def __call__(self, *args, output_subset=None, **kwargs): output_subset = [self.output_keys.index(key) for key in output_subset] # Reinitialize each container's 'provided' counter + if len(args) == 0: # for speed we trust the input for empty args + trust_input = True if trust_input: for arg_container, arg in zip(input_storage, args, strict=False): arg_container.storage[0] = arg From 1099ba45a59124ac86690b3677c7d7260f392fc3 Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Mon, 17 Feb 2025 20:51:12 +0530 Subject: [PATCH 02/11] Add trust_input parameter to function and pfunc for input validation control --- pytensor/compile/function/__init__.py | 16 ++++++++++++++-- pytensor/compile/function/pfunc.py | 6 ++++++ pytensor/compile/function/types.py | 24 +++++++++++++++++++----- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/pytensor/compile/function/__init__.py b/pytensor/compile/function/__init__.py index 7fa3a179ac..3b17a106fa 100644 --- a/pytensor/compile/function/__init__.py +++ b/pytensor/compile/function/__init__.py @@ -107,6 +107,7 @@ def function( allow_input_downcast: bool | None = None, profile: bool | ProfileStats | None = None, on_unused_input: str | None = None, + trust_input: bool = False, ): """ Return a :class:`callable object ` @@ -164,6 +165,10 @@ def function( on_unused_input What to do if a variable in the 'inputs' list is not used in the graph. Possible values are 'raise', 'warn', 'ignore' and None. + trust_input: bool + If True, the inputs are trusted to be correct. This is used to avoid + the overhead of checking the inputs for correctness. This should only + be used if the inputs are guaranteed to be correct. Returns ------- @@ -294,7 +299,8 @@ def opt_log1p(node): "contained in a list, even when there is a single " "input." ) - + if len(inputs) == 0: + trust_input = True # we can trust the empty input (for speed) # compute some features of the arguments: uses_tuple = any(isinstance(i, list | tuple) for i in inputs) uses_updates = bool(updates) @@ -310,7 +316,12 @@ def opt_log1p(node): "semantics, which disallow using updates and givens" ) fn = orig_function( - inputs, outputs, mode=mode, accept_inplace=accept_inplace, name=name + inputs, + outputs, + mode=mode, + accept_inplace=accept_inplace, + name=name, + trust_input=trust_input, ) else: # note: pfunc will also call orig_function -- orig_function is @@ -329,5 +340,6 @@ def opt_log1p(node): on_unused_input=on_unused_input, profile=profile, output_keys=output_keys, + trust_input=trust_input, ) return fn diff --git a/pytensor/compile/function/pfunc.py b/pytensor/compile/function/pfunc.py index b938cb6a55..a89eb4b4ba 100644 --- a/pytensor/compile/function/pfunc.py +++ b/pytensor/compile/function/pfunc.py @@ -377,6 +377,7 @@ def pfunc( on_unused_input=None, output_keys=None, fgraph: FunctionGraph | None = None, + trust_input: bool = False, ) -> Function: """ Function-constructor for graphs with shared variables. @@ -425,6 +426,10 @@ def pfunc( fgraph An existing `FunctionGraph` from which to construct the returned `Function`. When this is non-``None``, nothing is cloned. + trust_input : bool + If True, the inputs are trusted to be correct. This is used to avoid + the overhead of checking the inputs for correctness. This should only + be used if the inputs are guaranteed to be correct. Returns ------- @@ -472,6 +477,7 @@ def pfunc( on_unused_input=on_unused_input, output_keys=output_keys, fgraph=fgraph, + trust_input=trust_input, ) diff --git a/pytensor/compile/function/types.py b/pytensor/compile/function/types.py index 33642b7c2b..c521024ebf 100644 --- a/pytensor/compile/function/types.py +++ b/pytensor/compile/function/types.py @@ -373,6 +373,7 @@ def __init__( return_none: bool, output_keys, maker: "FunctionMaker", + trust_input: bool = False, name: str | None = None, ): """ @@ -407,6 +408,10 @@ def __init__( TODO maker The `FunctionMaker` that created this instance. + trust_input : bool + If True, the inputs are trusted to be correct. This is used to avoid + the overhead of checking the inputs for correctness. This should only + be used if the inputs are guaranteed to be correct. name A string name. """ @@ -420,7 +425,7 @@ def __init__( self.return_none = return_none self.maker = maker self.profile = None # reassigned in FunctionMaker.create - self.trust_input = False # If True, we don't check the input parameter + self.trust_input = trust_input # If True, we don't check the input parameter self.name = name self.nodes_with_inner_function = [] self.output_keys = output_keys @@ -916,8 +921,6 @@ def __call__(self, *args, output_subset=None, **kwargs): output_subset = [self.output_keys.index(key) for key in output_subset] # Reinitialize each container's 'provided' counter - if len(args) == 0: # for speed we trust the input for empty args - trust_input = True if trust_input: for arg_container, arg in zip(input_storage, args, strict=False): arg_container.storage[0] = arg @@ -1343,7 +1346,10 @@ class FunctionMaker: name : str An optional name for this function. If used, the profile mode will print the time spent in this function. - + trust_input : bool + If True, the inputs are trusted to be correct. This is used to avoid + the overhead of checking the inputs for correctness. This should only + be used if the inputs are guaranteed to be correct. """ @staticmethod @@ -1509,6 +1515,7 @@ def __init__( output_keys=None, name=None, no_fgraph_prep=False, + trust_input=False, ): # Save the provided mode, not the instantiated mode. # The instantiated mode don't pickle and if we unpickle an PyTensor @@ -1611,6 +1618,7 @@ def __init__( self.on_unused_input = on_unused_input # Used for the pickling/copy self.output_keys = output_keys self.name = name + self.trust_input = trust_input self.required = [(i.value is None) for i in self.inputs] self.refeed = [ @@ -1728,6 +1736,7 @@ def create(self, input_storage=None, storage_map=None): self.return_none, self.output_keys, self, + trust_input=self.trust_input, name=self.name, ) @@ -1745,6 +1754,7 @@ def orig_function( on_unused_input=None, output_keys=None, fgraph: FunctionGraph | None = None, + trust_input: bool = False, ) -> Function: """ Return a Function that will calculate the outputs from the inputs. @@ -1775,7 +1785,10 @@ def orig_function( fgraph An existing `FunctionGraph` to use instead of constructing a new one from cloned `outputs`. - + trust_input : bool + If True, the inputs are trusted to be correct. This is used to avoid + the overhead of checking the inputs for correctness. This should only + be used if the inputs are guaranteed to be correct. """ if profile: @@ -1808,6 +1821,7 @@ def orig_function( output_keys=output_keys, name=name, fgraph=fgraph, + trust_input=trust_input, ) with config.change_flags(compute_test_value="off"): fn = m.create(defaults) From 04bcc536c77911d2f695541357e75839cab9d830 Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Fri, 14 Feb 2025 03:18:13 +0530 Subject: [PATCH 03/11] Added an if condition to set trust_input to True if argument list is empty in __call__ function of Function class in pytensor/compile/function/types.py --- pytensor/compile/function/types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytensor/compile/function/types.py b/pytensor/compile/function/types.py index c521024ebf..24d28ba7b6 100644 --- a/pytensor/compile/function/types.py +++ b/pytensor/compile/function/types.py @@ -921,6 +921,8 @@ def __call__(self, *args, output_subset=None, **kwargs): output_subset = [self.output_keys.index(key) for key in output_subset] # Reinitialize each container's 'provided' counter + if len(args) == 0: # for speed we trust the input for empty args + trust_input = True if trust_input: for arg_container, arg in zip(input_storage, args, strict=False): arg_container.storage[0] = arg From 489ffed33c3e69ba72b46fe151b87f90b7f55777 Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Mon, 17 Feb 2025 21:00:03 +0530 Subject: [PATCH 04/11] Remove redundant trust_input check for empty args in Function class --- pytensor/compile/function/types.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytensor/compile/function/types.py b/pytensor/compile/function/types.py index 24d28ba7b6..c521024ebf 100644 --- a/pytensor/compile/function/types.py +++ b/pytensor/compile/function/types.py @@ -921,8 +921,6 @@ def __call__(self, *args, output_subset=None, **kwargs): output_subset = [self.output_keys.index(key) for key in output_subset] # Reinitialize each container's 'provided' counter - if len(args) == 0: # for speed we trust the input for empty args - trust_input = True if trust_input: for arg_container, arg in zip(input_storage, args, strict=False): arg_container.storage[0] = arg From 454c4c67517ff18829f23aee5efdb09548810419 Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Mon, 24 Feb 2025 11:50:04 +0530 Subject: [PATCH 05/11] Update trust_input parameter documentation to clarify input validation behavior --- pytensor/compile/function/__init__.py | 10 +++++---- pytensor/compile/function/pfunc.py | 10 +++++---- pytensor/compile/function/types.py | 30 ++++++++++++++++----------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/pytensor/compile/function/__init__.py b/pytensor/compile/function/__init__.py index 3b17a106fa..c95bc9fb13 100644 --- a/pytensor/compile/function/__init__.py +++ b/pytensor/compile/function/__init__.py @@ -165,10 +165,12 @@ def function( on_unused_input What to do if a variable in the 'inputs' list is not used in the graph. Possible values are 'raise', 'warn', 'ignore' and None. - trust_input: bool - If True, the inputs are trusted to be correct. This is used to avoid - the overhead of checking the inputs for correctness. This should only - be used if the inputs are guaranteed to be correct. + trust_input: bool, default False + If True, no input validation checks are performed when the function is + called. This includes checking the number of inputs, their types and + that multiple inputs are not aliased to each other. Failure to meet any + of these conditions can lead to computational errors or to the + interpreter crashing. Returns ------- diff --git a/pytensor/compile/function/pfunc.py b/pytensor/compile/function/pfunc.py index a89eb4b4ba..829fd28ec1 100644 --- a/pytensor/compile/function/pfunc.py +++ b/pytensor/compile/function/pfunc.py @@ -426,10 +426,12 @@ def pfunc( fgraph An existing `FunctionGraph` from which to construct the returned `Function`. When this is non-``None``, nothing is cloned. - trust_input : bool - If True, the inputs are trusted to be correct. This is used to avoid - the overhead of checking the inputs for correctness. This should only - be used if the inputs are guaranteed to be correct. + trust_input : bool, default False + If True, no input validation checks are performed when the function is + called. This includes checking the number of inputs, their types and + that multiple inputs are not aliased to each other. Failure to meet any + of these conditions can lead to computational errors or to the + interpreter crashing. Returns ------- diff --git a/pytensor/compile/function/types.py b/pytensor/compile/function/types.py index c521024ebf..69ba7a3bdc 100644 --- a/pytensor/compile/function/types.py +++ b/pytensor/compile/function/types.py @@ -408,10 +408,12 @@ def __init__( TODO maker The `FunctionMaker` that created this instance. - trust_input : bool - If True, the inputs are trusted to be correct. This is used to avoid - the overhead of checking the inputs for correctness. This should only - be used if the inputs are guaranteed to be correct. + trust_input : bool, default False + If True, no input validation checks are performed when the function is + called. This includes checking the number of inputs, their types and + that multiple inputs are not aliased to each other. Failure to meet any + of these conditions can lead to computational errors or to the + interpreter crashing. name A string name. """ @@ -1346,10 +1348,12 @@ class FunctionMaker: name : str An optional name for this function. If used, the profile mode will print the time spent in this function. - trust_input : bool - If True, the inputs are trusted to be correct. This is used to avoid - the overhead of checking the inputs for correctness. This should only - be used if the inputs are guaranteed to be correct. + trust_input : bool, default False + If True, no input validation checks are performed when the function is + called. This includes checking the number of inputs, their types and + that multiple inputs are not aliased to each other. Failure to meet any + of these conditions can lead to computational errors or to the + interpreter crashing. """ @staticmethod @@ -1785,10 +1789,12 @@ def orig_function( fgraph An existing `FunctionGraph` to use instead of constructing a new one from cloned `outputs`. - trust_input : bool - If True, the inputs are trusted to be correct. This is used to avoid - the overhead of checking the inputs for correctness. This should only - be used if the inputs are guaranteed to be correct. + trust_input : bool, default False + If True, no input validation checks are performed when the function is + called. This includes checking the number of inputs, their types and + that multiple inputs are not aliased to each other. Failure to meet any + of these conditions can lead to computational errors or to the + interpreter crashing. """ if profile: From 2949a3935e6c87972900e8fe67dec32777fe597d Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Mon, 24 Feb 2025 11:52:46 +0530 Subject: [PATCH 06/11] Fix formatting of trust_input parameter documentation for consistency --- pytensor/compile/function/__init__.py | 4 ++-- pytensor/compile/function/pfunc.py | 4 ++-- pytensor/compile/function/types.py | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pytensor/compile/function/__init__.py b/pytensor/compile/function/__init__.py index c95bc9fb13..819839d037 100644 --- a/pytensor/compile/function/__init__.py +++ b/pytensor/compile/function/__init__.py @@ -166,10 +166,10 @@ def function( What to do if a variable in the 'inputs' list is not used in the graph. Possible values are 'raise', 'warn', 'ignore' and None. trust_input: bool, default False - If True, no input validation checks are performed when the function is + If True, no input validation checks are performed when the function is called. This includes checking the number of inputs, their types and that multiple inputs are not aliased to each other. Failure to meet any - of these conditions can lead to computational errors or to the + of these conditions can lead to computational errors or to the interpreter crashing. Returns diff --git a/pytensor/compile/function/pfunc.py b/pytensor/compile/function/pfunc.py index 829fd28ec1..749ec5cb42 100644 --- a/pytensor/compile/function/pfunc.py +++ b/pytensor/compile/function/pfunc.py @@ -427,10 +427,10 @@ def pfunc( An existing `FunctionGraph` from which to construct the returned `Function`. When this is non-``None``, nothing is cloned. trust_input : bool, default False - If True, no input validation checks are performed when the function is + If True, no input validation checks are performed when the function is called. This includes checking the number of inputs, their types and that multiple inputs are not aliased to each other. Failure to meet any - of these conditions can lead to computational errors or to the + of these conditions can lead to computational errors or to the interpreter crashing. Returns diff --git a/pytensor/compile/function/types.py b/pytensor/compile/function/types.py index 69ba7a3bdc..9cc85f3d24 100644 --- a/pytensor/compile/function/types.py +++ b/pytensor/compile/function/types.py @@ -409,10 +409,10 @@ def __init__( maker The `FunctionMaker` that created this instance. trust_input : bool, default False - If True, no input validation checks are performed when the function is + If True, no input validation checks are performed when the function is called. This includes checking the number of inputs, their types and that multiple inputs are not aliased to each other. Failure to meet any - of these conditions can lead to computational errors or to the + of these conditions can lead to computational errors or to the interpreter crashing. name A string name. @@ -1349,10 +1349,10 @@ class FunctionMaker: An optional name for this function. If used, the profile mode will print the time spent in this function. trust_input : bool, default False - If True, no input validation checks are performed when the function is + If True, no input validation checks are performed when the function is called. This includes checking the number of inputs, their types and that multiple inputs are not aliased to each other. Failure to meet any - of these conditions can lead to computational errors or to the + of these conditions can lead to computational errors or to the interpreter crashing. """ @@ -1790,10 +1790,10 @@ def orig_function( An existing `FunctionGraph` to use instead of constructing a new one from cloned `outputs`. trust_input : bool, default False - If True, no input validation checks are performed when the function is + If True, no input validation checks are performed when the function is called. This includes checking the number of inputs, their types and that multiple inputs are not aliased to each other. Failure to meet any - of these conditions can lead to computational errors or to the + of these conditions can lead to computational errors or to the interpreter crashing. """ From 4499ab7d0472013eb8f135be25f105b0553a6311 Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Tue, 25 Feb 2025 01:31:27 +0530 Subject: [PATCH 07/11] Add tests for trust_input behavior with empty and non-empty inputs --- tests/compile/function/test_function.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/compile/function/test_function.py b/tests/compile/function/test_function.py index 9f75ef15d8..bfdba8efe6 100644 --- a/tests/compile/function/test_function.py +++ b/tests/compile/function/test_function.py @@ -54,6 +54,22 @@ def test_function_name(): assert regex.match(func.name) is not None +def test_trust_empty_input(): + x = shared(1) + f = function([], x + 1) + assert f.trust_input is True + + +def test_trust_input(): + x = dvector() + y = shared(1) + z = x + y + f = function([x], z) + assert f.trust_input is False + f = function([x], z, trust_input=True) + assert f.trust_input is True + + class TestFunctionIn: def test_in_strict(self): a = dvector() From 289eec900a10d5e63d88cd5b34f055b78ec6b986 Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Wed, 26 Feb 2025 10:23:01 +0530 Subject: [PATCH 08/11] Fix formatting in test_trust_input function --- tests/compile/function/test_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile/function/test_function.py b/tests/compile/function/test_function.py index bfdba8efe6..c8965e8ea9 100644 --- a/tests/compile/function/test_function.py +++ b/tests/compile/function/test_function.py @@ -64,7 +64,7 @@ def test_trust_input(): x = dvector() y = shared(1) z = x + y - f = function([x], z) + f = function([x], z) assert f.trust_input is False f = function([x], z, trust_input=True) assert f.trust_input is True From 7ee1add8fc2c354f5d3995d012b9879f1fee881b Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Wed, 26 Feb 2025 13:13:50 +0530 Subject: [PATCH 09/11] Add trust_input parameter to function_dump and _Maker class --- pytensor/compile/debugmode.py | 8 ++++++++ pytensor/compile/function/__init__.py | 2 ++ 2 files changed, 10 insertions(+) diff --git a/pytensor/compile/debugmode.py b/pytensor/compile/debugmode.py index 5c51222a1b..384f9eb874 100644 --- a/pytensor/compile/debugmode.py +++ b/pytensor/compile/debugmode.py @@ -1966,6 +1966,12 @@ class _Maker(FunctionMaker): # inheritance buys a few helper functions If the outputs argument for pytensor.function was a list, then output_keys is None. If the outputs argument was a dict, then output_keys is a sorted list of the keys from that dict. + trust_input : bool, default False + If True, no input validation checks are performed when the function is + called. This includes checking the number of inputs, their types and + that multiple inputs are not aliased to each other. Failure to meet any + of these conditions can lead to computational errors or to the + interpreter crashing. Notes ----- @@ -1993,6 +1999,7 @@ def __init__( output_keys=None, name=None, no_fgraph_prep=False, + trust_input=False, ): self.mode = mode self.profile = profile @@ -2146,6 +2153,7 @@ def __init__( self.on_unused_input = on_unused_input # Used for the pickling/copy self.output_keys = output_keys self.name = name + self.trust_input = trust_input self.required = [(i.value is None) for i in self.inputs] self.refeed = [ diff --git a/pytensor/compile/function/__init__.py b/pytensor/compile/function/__init__.py index 819839d037..75c731da89 100644 --- a/pytensor/compile/function/__init__.py +++ b/pytensor/compile/function/__init__.py @@ -37,6 +37,7 @@ def function_dump( profile: bool | ProfileStats | None = None, on_unused_input: str | None = None, extra_tag_to_remove: str | None = None, + trust_input: bool = False, ): """ This is helpful to make a reproducible case for problems during PyTensor @@ -82,6 +83,7 @@ def function_dump( "allow_input_downcast": allow_input_downcast, "profile": profile, "on_unused_input": on_unused_input, + "trust_input": trust_input, } with Path(filename).open("wb") as f: pickler = pytensor.misc.pkl_utils.StripPickler( From 2b124e8924a820d333f9fa10af51837620571e8d Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Thu, 27 Feb 2025 23:14:10 +0530 Subject: [PATCH 10/11] Remove trust_input assignment for empty inputs in function definition --- pytensor/compile/function/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pytensor/compile/function/__init__.py b/pytensor/compile/function/__init__.py index 75c731da89..61e4aa3cfe 100644 --- a/pytensor/compile/function/__init__.py +++ b/pytensor/compile/function/__init__.py @@ -303,8 +303,7 @@ def opt_log1p(node): "contained in a list, even when there is a single " "input." ) - if len(inputs) == 0: - trust_input = True # we can trust the empty input (for speed) + # compute some features of the arguments: uses_tuple = any(isinstance(i, list | tuple) for i in inputs) uses_updates = bool(updates) From d13584a1d01e0db8bb05517acf830afa35dc3a48 Mon Sep 17 00:00:00 2001 From: Aarsh-Wankar <23110003@iitgn.ac.in> Date: Thu, 27 Feb 2025 23:55:48 +0530 Subject: [PATCH 11/11] Remove test for trust_input with empty input --- tests/compile/function/test_function.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/compile/function/test_function.py b/tests/compile/function/test_function.py index c8965e8ea9..d1f94dd689 100644 --- a/tests/compile/function/test_function.py +++ b/tests/compile/function/test_function.py @@ -54,12 +54,6 @@ def test_function_name(): assert regex.match(func.name) is not None -def test_trust_empty_input(): - x = shared(1) - f = function([], x + 1) - assert f.trust_input is True - - def test_trust_input(): x = dvector() y = shared(1)