提交 ffb72e42 authored 作者: Virgile Andreani's avatar Virgile Andreani 提交者: Thomas Wiecki

Enable ruff to format code in docstrings

上级 86c8a00f
...@@ -124,6 +124,9 @@ testpaths = "tests/" ...@@ -124,6 +124,9 @@ testpaths = "tests/"
line-length = 88 line-length = 88
exclude = ["doc/", "pytensor/_version.py"] exclude = ["doc/", "pytensor/_version.py"]
[tool.ruff.format]
docstring-code-format = true
[tool.ruff.lint] [tool.ruff.lint]
select = ["C", "E", "F", "I", "UP", "W", "RUF", "PERF", "PTH", "ISC"] select = ["C", "E", "F", "I", "UP", "W", "RUF", "PERF", "PTH", "ISC"]
ignore = ["C408", "C901", "E501", "E741", "RUF012", "PERF203", "ISC001"] ignore = ["C408", "C901", "E501", "E741", "RUF012", "PERF203", "ISC001"]
......
...@@ -190,7 +190,8 @@ class OpFromGraph(Op, HasInnerGraph): ...@@ -190,7 +190,8 @@ class OpFromGraph(Op, HasInnerGraph):
from pytensor import function, tensor as pt from pytensor import function, tensor as pt
from pytensor.compile.builders import OpFromGraph from pytensor.compile.builders import OpFromGraph
x, y, z = pt.scalars('xyz')
x, y, z = pt.scalars("xyz")
e = x + y * z e = x + y * z
op = OpFromGraph([x, y, z], [e]) op = OpFromGraph([x, y, z], [e])
# op behaves like a normal pytensor op # op behaves like a normal pytensor op
...@@ -206,7 +207,7 @@ class OpFromGraph(Op, HasInnerGraph): ...@@ -206,7 +207,7 @@ class OpFromGraph(Op, HasInnerGraph):
from pytensor import config, function, tensor as pt from pytensor import config, function, tensor as pt
from pytensor.compile.builders import OpFromGraph from pytensor.compile.builders import OpFromGraph
x, y, z = pt.scalars('xyz') x, y, z = pt.scalars("xyz")
s = pytensor.shared(np.random.random((2, 2)).astype(config.floatX)) s = pytensor.shared(np.random.random((2, 2)).astype(config.floatX))
e = x + y * z + s e = x + y * z + s
op = OpFromGraph([x, y, z], [e]) op = OpFromGraph([x, y, z], [e])
...@@ -221,12 +222,16 @@ class OpFromGraph(Op, HasInnerGraph): ...@@ -221,12 +222,16 @@ class OpFromGraph(Op, HasInnerGraph):
from pytensor import function, tensor as pt, grad from pytensor import function, tensor as pt, grad
from pytensor.compile.builders import OpFromGraph from pytensor.compile.builders import OpFromGraph
x, y, z = pt.scalars('xyz') x, y, z = pt.scalars("xyz")
e = x + y * z e = x + y * z
def rescale_dy(inps, outputs, out_grads): def rescale_dy(inps, outputs, out_grads):
x, y, z = inps x, y, z = inps
g, = out_grads (g,) = out_grads
return z*2 return z * 2
op = OpFromGraph( op = OpFromGraph(
[x, y, z], [x, y, z],
[e], [e],
...@@ -236,7 +241,7 @@ class OpFromGraph(Op, HasInnerGraph): ...@@ -236,7 +241,7 @@ class OpFromGraph(Op, HasInnerGraph):
dx, dy, dz = grad(e2, [x, y, z]) dx, dy, dz = grad(e2, [x, y, z])
fn = function([x, y, z], [dx, dy, dz]) fn = function([x, y, z], [dx, dy, dz])
# the gradient wrt y is now doubled # the gradient wrt y is now doubled
fn(2., 3., 4.) # [1., 8., 3.] fn(2.0, 3.0, 4.0) # [1., 8., 3.]
""" """
......
...@@ -692,25 +692,24 @@ def subgraph_grad(wrt, end, start=None, cost=None, details=False): ...@@ -692,25 +692,24 @@ def subgraph_grad(wrt, end, start=None, cost=None, details=False):
.. code-block:: python .. code-block:: python
x, t = pytensor.tensor.fvector('x'), pytensor.tensor.fvector('t') x, t = pytensor.tensor.fvector("x"), pytensor.tensor.fvector("t")
w1 = pytensor.shared(np.random.standard_normal((3,4))) w1 = pytensor.shared(np.random.standard_normal((3, 4)))
w2 = pytensor.shared(np.random.standard_normal((4,2))) w2 = pytensor.shared(np.random.standard_normal((4, 2)))
a1 = pytensor.tensor.tanh(pytensor.tensor.dot(x,w1)) a1 = pytensor.tensor.tanh(pytensor.tensor.dot(x, w1))
a2 = pytensor.tensor.tanh(pytensor.tensor.dot(a1,w2)) a2 = pytensor.tensor.tanh(pytensor.tensor.dot(a1, w2))
cost2 = pytensor.tensor.sqr(a2 - t).sum() cost2 = pytensor.tensor.sqr(a2 - t).sum()
cost2 += pytensor.tensor.sqr(w2.sum()) cost2 += pytensor.tensor.sqr(w2.sum())
cost1 = pytensor.tensor.sqr(w1.sum()) cost1 = pytensor.tensor.sqr(w1.sum())
params = [[w2],[w1]] params = [[w2], [w1]]
costs = [cost2,cost1] costs = [cost2, cost1]
grad_ends = [[a1], [x]] grad_ends = [[a1], [x]]
next_grad = None next_grad = None
param_grads = [] param_grads = []
for i in range(2): for i in range(2):
param_grad, next_grad = pytensor.subgraph_grad( param_grad, next_grad = pytensor.subgraph_grad(
wrt=params[i], end=grad_ends[i], wrt=params[i], end=grad_ends[i], start=next_grad, cost=costs[i]
start=next_grad, cost=costs[i]
) )
next_grad = dict(zip(grad_ends[i], next_grad)) next_grad = dict(zip(grad_ends[i], next_grad))
param_grads.extend(param_grad) param_grads.extend(param_grad)
...@@ -1704,9 +1703,11 @@ def verify_grad( ...@@ -1704,9 +1703,11 @@ def verify_grad(
Examples Examples
-------- --------
>>> verify_grad(pytensor.tensor.tanh, >>> verify_grad(
... (np.asarray([[2, 3, 4], [-1, 3.3, 9.9]]),), ... pytensor.tensor.tanh,
... rng=np.random.default_rng(23098)) ... (np.asarray([[2, 3, 4], [-1, 3.3, 9.9]]),),
... rng=np.random.default_rng(23098),
... )
Parameters Parameters
---------- ----------
...@@ -2342,9 +2343,9 @@ def grad_clip(x, lower_bound, upper_bound): ...@@ -2342,9 +2343,9 @@ def grad_clip(x, lower_bound, upper_bound):
Examples Examples
-------- --------
>>> x = pytensor.tensor.type.scalar() >>> x = pytensor.tensor.type.scalar()
>>> z = pytensor.gradient.grad(grad_clip(x, -1, 1)**2, x) >>> z = pytensor.gradient.grad(grad_clip(x, -1, 1) ** 2, x)
>>> z2 = pytensor.gradient.grad(x**2, x) >>> z2 = pytensor.gradient.grad(x**2, x)
>>> f = pytensor.function([x], outputs = [z, z2]) >>> f = pytensor.function([x], outputs=[z, z2])
>>> print(f(2.0)) >>> print(f(2.0))
[array(1.), array(4.)] [array(1.), array(4.)]
...@@ -2383,7 +2384,7 @@ def grad_scale(x, multiplier): ...@@ -2383,7 +2384,7 @@ def grad_scale(x, multiplier):
>>> fprime = pytensor.function([x], fp) >>> fprime = pytensor.function([x], fp)
>>> print(fprime(2)) # doctest: +ELLIPSIS >>> print(fprime(2)) # doctest: +ELLIPSIS
-0.416... -0.416...
>>> f_inverse=grad_scale(fx, -1.) >>> f_inverse = grad_scale(fx, -1.0)
>>> fpp = pytensor.grad(f_inverse, wrt=x) >>> fpp = pytensor.grad(f_inverse, wrt=x)
>>> fpprime = pytensor.function([x], fpp) >>> fpprime = pytensor.function([x], fpp)
>>> print(fpprime(2)) # doctest: +ELLIPSIS >>> print(fpprime(2)) # doctest: +ELLIPSIS
......
...@@ -399,18 +399,24 @@ class Variable(Node, Generic[_TypeType, OptionalApplyType]): ...@@ -399,18 +399,24 @@ class Variable(Node, Generic[_TypeType, OptionalApplyType]):
import pytensor import pytensor
import pytensor.tensor as pt import pytensor.tensor as pt
a = pt.constant(1.5) # declare a symbolic constant a = pt.constant(1.5) # declare a symbolic constant
b = pt.fscalar() # declare a symbolic floating-point scalar b = pt.fscalar() # declare a symbolic floating-point scalar
c = a + b # create a simple expression c = a + b # create a simple expression
f = pytensor.function([b], [c]) # this works because a has a value associated with it already f = pytensor.function(
[b], [c]
) # this works because a has a value associated with it already
assert 4.0 == f(2.5) # bind 2.5 to an internal copy of b and evaluate an internal c assert 4.0 == f(2.5) # bind 2.5 to an internal copy of b and evaluate an internal c
pytensor.function([a], [c]) # compilation error because b (required by c) is undefined pytensor.function(
[a], [c]
) # compilation error because b (required by c) is undefined
pytensor.function([a,b], [c]) # compilation error because a is constant, it can't be an input pytensor.function(
[a, b], [c]
) # compilation error because a is constant, it can't be an input
The python variables ``a, b, c`` all refer to instances of type The python variables ``a, b, c`` all refer to instances of type
...@@ -587,10 +593,10 @@ class Variable(Node, Generic[_TypeType, OptionalApplyType]): ...@@ -587,10 +593,10 @@ class Variable(Node, Generic[_TypeType, OptionalApplyType]):
>>> import numpy as np >>> import numpy as np
>>> import pytensor.tensor as pt >>> import pytensor.tensor as pt
>>> x = pt.dscalar('x') >>> x = pt.dscalar("x")
>>> y = pt.dscalar('y') >>> y = pt.dscalar("y")
>>> z = x + y >>> z = x + y
>>> np.allclose(z.eval({x : 16.3, y : 12.1}), 28.4) >>> np.allclose(z.eval({x: 16.3, y: 12.1}), 28.4)
True True
We passed :meth:`eval` a dictionary mapping symbolic PyTensor We passed :meth:`eval` a dictionary mapping symbolic PyTensor
...@@ -963,9 +969,9 @@ def explicit_graph_inputs( ...@@ -963,9 +969,9 @@ def explicit_graph_inputs(
import pytensor.tensor as pt import pytensor.tensor as pt
from pytensor.graph.basic import explicit_graph_inputs from pytensor.graph.basic import explicit_graph_inputs
x = pt.vector('x') x = pt.vector("x")
y = pt.constant(2) y = pt.constant(2)
z = pt.mul(x*y) z = pt.mul(x * y)
inputs = list(explicit_graph_inputs(z)) inputs = list(explicit_graph_inputs(z))
f = pytensor.function(inputs, z) f = pytensor.function(inputs, z)
...@@ -1041,7 +1047,7 @@ def orphans_between( ...@@ -1041,7 +1047,7 @@ def orphans_between(
>>> from pytensor.graph.basic import orphans_between >>> from pytensor.graph.basic import orphans_between
>>> from pytensor.tensor import scalars >>> from pytensor.tensor import scalars
>>> x, y = scalars("xy") >>> x, y = scalars("xy")
>>> list(orphans_between([x], [(x+y)])) >>> list(orphans_between([x], [(x + y)]))
[y] [y]
""" """
......
...@@ -30,7 +30,7 @@ class CLinkerObject: ...@@ -30,7 +30,7 @@ class CLinkerObject:
.. code-block:: python .. code-block:: python
def c_headers(self, **kwargs): def c_headers(self, **kwargs):
return ['<iostream>', '<math.h>', '/full/path/to/header.h'] return ["<iostream>", "<math.h>", "/full/path/to/header.h"]
""" """
...@@ -54,7 +54,7 @@ class CLinkerObject: ...@@ -54,7 +54,7 @@ class CLinkerObject:
.. code-block:: python .. code-block:: python
def c_header_dirs(self, **kwargs): def c_header_dirs(self, **kwargs):
return ['/usr/local/include', '/opt/weirdpath/src/include'] return ["/usr/local/include", "/opt/weirdpath/src/include"]
""" """
return [] return []
...@@ -134,7 +134,7 @@ class CLinkerObject: ...@@ -134,7 +134,7 @@ class CLinkerObject:
.. code-block:: python .. code-block:: python
def c_compile_args(self, **kwargs): def c_compile_args(self, **kwargs):
return ['-ffast-math'] return ["-ffast-math"]
""" """
return [] return []
......
...@@ -29,7 +29,9 @@ In your Op sub-class: ...@@ -29,7 +29,9 @@ In your Op sub-class:
.. code-block:: python .. code-block:: python
params_type = ParamsType(attr1=TensorType('int32', shape=(None, None)), attr2=ScalarType('float64')) params_type = ParamsType(
attr1=TensorType("int32", shape=(None, None)), attr2=ScalarType("float64")
)
If your op contains attributes ``attr1`` **and** ``attr2``, the default ``op.get_params()`` If your op contains attributes ``attr1`` **and** ``attr2``, the default ``op.get_params()``
implementation will automatically try to look for it and generate an appropriate Params object. implementation will automatically try to look for it and generate an appropriate Params object.
...@@ -77,26 +79,35 @@ enumerations will be directly available as ParamsType attributes. ...@@ -77,26 +79,35 @@ enumerations will be directly available as ParamsType attributes.
from pytensor.link.c.params_type import ParamsType from pytensor.link.c.params_type import ParamsType
from pytensor.link.c.type import EnumType, EnumList from pytensor.link.c.type import EnumType, EnumList
wrapper = ParamsType(enum1=EnumList('CONSTANT_1', 'CONSTANT_2', 'CONSTANT_3'), wrapper = ParamsType(
enum2=EnumType(PI=3.14, EPSILON=0.001)) enum1=EnumList("CONSTANT_1", "CONSTANT_2", "CONSTANT_3"),
enum2=EnumType(PI=3.14, EPSILON=0.001),
)
# Each enum constant is available as a wrapper attribute: # Each enum constant is available as a wrapper attribute:
print(wrapper.CONSTANT_1, wrapper.CONSTANT_2, wrapper.CONSTANT_3, print(
wrapper.PI, wrapper.EPSILON) wrapper.CONSTANT_1,
wrapper.CONSTANT_2,
wrapper.CONSTANT_3,
wrapper.PI,
wrapper.EPSILON,
)
# For convenience, you can also look for a constant by name with # For convenience, you can also look for a constant by name with
# ``ParamsType.get_enum()`` method. # ``ParamsType.get_enum()`` method.
pi = wrapper.get_enum('PI') pi = wrapper.get_enum("PI")
epsilon = wrapper.get_enum('EPSILON') epsilon = wrapper.get_enum("EPSILON")
constant_2 = wrapper.get_enum('CONSTANT_2') constant_2 = wrapper.get_enum("CONSTANT_2")
print(pi, epsilon, constant_2) print(pi, epsilon, constant_2)
This implies that a ParamsType cannot contain different enum types with common enum names:: This implies that a ParamsType cannot contain different enum types with common enum names::
# Following line will raise an error, # Following line will raise an error,
# as there is a "CONSTANT_1" defined both in enum1 and enum2. # as there is a "CONSTANT_1" defined both in enum1 and enum2.
wrapper = ParamsType(enum1=EnumList('CONSTANT_1', 'CONSTANT_2'), wrapper = ParamsType(
enum2=EnumType(CONSTANT_1=0, CONSTANT_3=5)) enum1=EnumList("CONSTANT_1", "CONSTANT_2"),
enum2=EnumType(CONSTANT_1=0, CONSTANT_3=5),
)
If your enum types contain constant aliases, you can retrieve them from ParamsType If your enum types contain constant aliases, you can retrieve them from ParamsType
with ``ParamsType.enum_from_alias(alias)`` method (see :class:`pytensor.link.c.type.EnumType` with ``ParamsType.enum_from_alias(alias)`` method (see :class:`pytensor.link.c.type.EnumType`
...@@ -104,11 +115,12 @@ for more info about enumeration aliases). ...@@ -104,11 +115,12 @@ for more info about enumeration aliases).
.. code-block:: python .. code-block:: python
wrapper = ParamsType(enum1=EnumList('A', ('B', 'beta'), 'C'), wrapper = ParamsType(
enum2=EnumList(('D', 'delta'), 'E', 'F')) enum1=EnumList("A", ("B", "beta"), "C"), enum2=EnumList(("D", "delta"), "E", "F")
)
b1 = wrapper.B b1 = wrapper.B
b2 = wrapper.get_enum('B') b2 = wrapper.get_enum("B")
b3 = wrapper.enum_from_alias('beta') b3 = wrapper.enum_from_alias("beta")
assert b1 == b2 == b3 assert b1 == b2 == b3
""" """
...@@ -236,10 +248,13 @@ class Params(dict): ...@@ -236,10 +248,13 @@ class Params(dict):
from pytensor.link.c.params_type import ParamsType, Params from pytensor.link.c.params_type import ParamsType, Params
from pytensor.scalar import ScalarType from pytensor.scalar import ScalarType
# You must create a ParamsType first: # You must create a ParamsType first:
params_type = ParamsType(attr1=ScalarType('int32'), params_type = ParamsType(
key2=ScalarType('float32'), attr1=ScalarType("int32"),
field3=ScalarType('int64')) key2=ScalarType("float32"),
field3=ScalarType("int64"),
)
# Then you can create a Params object with # Then you can create a Params object with
# the params type defined above and values for attributes. # the params type defined above and values for attributes.
params = Params(params_type, attr1=1, key2=2.0, field3=3) params = Params(params_type, attr1=1, key2=2.0, field3=3)
...@@ -491,11 +506,13 @@ class ParamsType(CType): ...@@ -491,11 +506,13 @@ class ParamsType(CType):
from pytensor.link.c.type import EnumType, EnumList from pytensor.link.c.type import EnumType, EnumList
from pytensor.scalar import ScalarType from pytensor.scalar import ScalarType
wrapper = ParamsType(scalar=ScalarType('int32'), wrapper = ParamsType(
letters=EnumType(A=1, B=2, C=3), scalar=ScalarType("int32"),
digits=EnumList('ZERO', 'ONE', 'TWO')) letters=EnumType(A=1, B=2, C=3),
print(wrapper.get_enum('C')) # 3 digits=EnumList("ZERO", "ONE", "TWO"),
print(wrapper.get_enum('TWO')) # 2 )
print(wrapper.get_enum("C")) # 3
print(wrapper.get_enum("TWO")) # 2
# You can also directly do: # You can also directly do:
print(wrapper.C) print(wrapper.C)
...@@ -520,17 +537,19 @@ class ParamsType(CType): ...@@ -520,17 +537,19 @@ class ParamsType(CType):
from pytensor.link.c.type import EnumType, EnumList from pytensor.link.c.type import EnumType, EnumList
from pytensor.scalar import ScalarType from pytensor.scalar import ScalarType
wrapper = ParamsType(scalar=ScalarType('int32'), wrapper = ParamsType(
letters=EnumType(A=(1, 'alpha'), B=(2, 'beta'), C=3), scalar=ScalarType("int32"),
digits=EnumList(('ZERO', 'nothing'), ('ONE', 'unit'), ('TWO', 'couple'))) letters=EnumType(A=(1, "alpha"), B=(2, "beta"), C=3),
print(wrapper.get_enum('C')) # 3 digits=EnumList(("ZERO", "nothing"), ("ONE", "unit"), ("TWO", "couple")),
print(wrapper.get_enum('TWO')) # 2 )
print(wrapper.enum_from_alias('alpha')) # 1 print(wrapper.get_enum("C")) # 3
print(wrapper.enum_from_alias('nothing')) # 0 print(wrapper.get_enum("TWO")) # 2
print(wrapper.enum_from_alias("alpha")) # 1
print(wrapper.enum_from_alias("nothing")) # 0
# For the following, alias 'C' is not defined, so the method looks for # For the following, alias 'C' is not defined, so the method looks for
# a constant named 'C', and finds it. # a constant named 'C', and finds it.
print(wrapper.enum_from_alias('C')) # 3 print(wrapper.enum_from_alias("C")) # 3
.. note:: .. note::
...@@ -567,12 +586,14 @@ class ParamsType(CType): ...@@ -567,12 +586,14 @@ class ParamsType(CType):
from pytensor.tensor.type import dmatrix from pytensor.tensor.type import dmatrix
from pytensor.scalar import ScalarType from pytensor.scalar import ScalarType
class MyObject: class MyObject:
def __init__(self): def __init__(self):
self.a = 10 self.a = 10
self.b = numpy.asarray([[1, 2, 3], [4, 5, 6]]) self.b = numpy.asarray([[1, 2, 3], [4, 5, 6]])
params_type = ParamsType(a=ScalarType('int32'), b=dmatrix, c=ScalarType('bool'))
params_type = ParamsType(a=ScalarType("int32"), b=dmatrix, c=ScalarType("bool"))
o = MyObject() o = MyObject()
value_for_c = False value_for_c = False
......
...@@ -318,7 +318,7 @@ class EnumType(CType, dict): ...@@ -318,7 +318,7 @@ class EnumType(CType, dict):
.. code-block:: python .. code-block:: python
enum = EnumType(CONSTANT_1=1, CONSTANT_2=2.5, CONSTANT_3=False, CONSTANT_4=True) enum = EnumType(CONSTANT_1=1, CONSTANT_2=2.5, CONSTANT_3=False, CONSTANT_4=True)
print (enum.CONSTANT_1, enum.CONSTANT_2, enum.CONSTANT_3, enum.CONSTANT_4) print(enum.CONSTANT_1, enum.CONSTANT_2, enum.CONSTANT_3, enum.CONSTANT_4)
# will print 1 2.5 0 1 # will print 1 2.5 0 1
In C code: In C code:
...@@ -334,7 +334,7 @@ class EnumType(CType, dict): ...@@ -334,7 +334,7 @@ class EnumType(CType, dict):
.. code-block:: python .. code-block:: python
enum = EnumType(CONSTANT_1=0, CONSTANT_2=1, CONSTANT_3=2, ctype='size_t') enum = EnumType(CONSTANT_1=0, CONSTANT_2=1, CONSTANT_3=2, ctype="size_t")
# In C code, the Op param will then be a ``size_t``. # In C code, the Op param will then be a ``size_t``.
.. note:: .. note::
...@@ -349,8 +349,9 @@ class EnumType(CType, dict): ...@@ -349,8 +349,9 @@ class EnumType(CType, dict):
.. code-block:: python .. code-block:: python
enum = EnumType(CONSTANT_1=0, CONSTANT_2=1, CONSTANT_3=2, enum = EnumType(
ctype='size_t', cname='MyEnumName') CONSTANT_1=0, CONSTANT_2=1, CONSTANT_3=2, ctype="size_t", cname="MyEnumName"
)
**Example with aliases** **Example with aliases**
...@@ -359,7 +360,7 @@ class EnumType(CType, dict): ...@@ -359,7 +360,7 @@ class EnumType(CType, dict):
To give an alias to a constant in the EnumType constructor, use the following key-value syntax:: To give an alias to a constant in the EnumType constructor, use the following key-value syntax::
constant_name=(constant_alias, constant_value) constant_name = (constant_alias, constant_value)
You can then retrieve a constant from an alias with method ``EnumType.fromalias()``. You can then retrieve a constant from an alias with method ``EnumType.fromalias()``.
...@@ -372,23 +373,23 @@ class EnumType(CType, dict): ...@@ -372,23 +373,23 @@ class EnumType(CType, dict):
from pytensor.link.c.type import EnumType from pytensor.link.c.type import EnumType
# You can remark that constant 'C' does not have an alias. # You can remark that constant 'C' does not have an alias.
enum = EnumType(A=('alpha', 1), B=('beta', 2), C=3, D=('delta', 4)) enum = EnumType(A=("alpha", 1), B=("beta", 2), C=3, D=("delta", 4))
# Constants are all directly available by name. # Constants are all directly available by name.
print(enum.A, enum.B, enum.C, enum.D) print(enum.A, enum.B, enum.C, enum.D)
# But we can also now get some constants by alias. # But we can also now get some constants by alias.
a = enum.fromalias('alpha') a = enum.fromalias("alpha")
b = enum.fromalias('beta') b = enum.fromalias("beta")
d = enum.fromalias('delta') d = enum.fromalias("delta")
# If method fromalias() receives an unknown alias, # If method fromalias() receives an unknown alias,
# it will looks for a constant with this alias # it will looks for a constant with this alias
# as exact constant name. # as exact constant name.
c = enum.fromalias('C') # will get enum.C c = enum.fromalias("C") # will get enum.C
# An alias defined in an EnumType will be correctly converted with non-strict filtering. # An alias defined in an EnumType will be correctly converted with non-strict filtering.
value = enum.filter('delta', strict=False) value = enum.filter("delta", strict=False)
# value now contains enum.D, ie. 4. # value now contains enum.D, ie. 4.
.. note:: .. note::
...@@ -648,14 +649,24 @@ class EnumList(EnumType): ...@@ -648,14 +649,24 @@ class EnumList(EnumType):
Example:: Example::
enum = EnumList('CONSTANT_1', 'CONSTANT_2', 'CONSTANT_3', 'CONSTANT_4', 'CONSTANT_5') enum = EnumList(
print (enum.CONSTANT_1, enum.CONSTANT_2, enum.CONSTANT_3, enum.CONSTANT_4, enum.CONSTANT_5) "CONSTANT_1", "CONSTANT_2", "CONSTANT_3", "CONSTANT_4", "CONSTANT_5"
)
print(
enum.CONSTANT_1,
enum.CONSTANT_2,
enum.CONSTANT_3,
enum.CONSTANT_4,
enum.CONSTANT_5,
)
# will print: 0 1 2 3 4 # will print: 0 1 2 3 4
Like :class:`EnumType`, you can also define the C type and a C name for the op param. Like :class:`EnumType`, you can also define the C type and a C name for the op param.
Default C type is ``int``:: Default C type is ``int``::
enum = EnumList('CONSTANT_1', 'CONSTANT_2', 'CONSTANT_3', 'CONSTANT_4', ctype='unsigned int') enum = EnumList(
"CONSTANT_1", "CONSTANT_2", "CONSTANT_3", "CONSTANT_4", ctype="unsigned int"
)
Like :class:`EnumType`, you can also add an alias to a constant, by replacing the only constant name Like :class:`EnumType`, you can also add an alias to a constant, by replacing the only constant name
(e.g. ``'CONSTANT_NAME'``) by a couple with constant name first and constant alias second (e.g. ``'CONSTANT_NAME'``) by a couple with constant name first and constant alias second
...@@ -663,7 +674,7 @@ class EnumList(EnumType): ...@@ -663,7 +674,7 @@ class EnumList(EnumType):
.. code-block:: python .. code-block:: python
enum = EnumList(('A', 'alpha'), ('B', 'beta'), 'C', 'D', 'E', 'F', ('G', 'gamma')) enum = EnumList(("A", "alpha"), ("B", "beta"), "C", "D", "E", "F", ("G", "gamma"))
See test class :class:`tests.graph.test_types.TestOpEnumList` for a working example. See test class :class:`tests.graph.test_types.TestOpEnumList` for a working example.
...@@ -727,7 +738,9 @@ class CEnumType(EnumList): ...@@ -727,7 +738,9 @@ class CEnumType(EnumList):
.. code-block:: python .. code-block:: python
enum = CEnumType('CONSTANT_CNAME_1', 'CONSTANT_CNAME_2', 'CONSTANT_CNAME_3', ctype='long') enum = CEnumType(
"CONSTANT_CNAME_1", "CONSTANT_CNAME_2", "CONSTANT_CNAME_3", ctype="long"
)
Like :class:`EnumList`, you can also add an alias to a constant, with same syntax as in :class:`EnumList`. Like :class:`EnumList`, you can also add an alias to a constant, with same syntax as in :class:`EnumList`.
......
...@@ -185,7 +185,9 @@ def create_axis_reducer( ...@@ -185,7 +185,9 @@ def create_axis_reducer(
.. code-block:: python .. code-block:: python
def careduce_axis(x): def careduce_axis(x):
res_shape = tuple(shape[i] if i < axis else shape[i + 1] for i in range(ndim - 1)) res_shape = tuple(
shape[i] if i < axis else shape[i + 1] for i in range(ndim - 1)
)
res = np.full(res_shape, identity, dtype=dtype) res = np.full(res_shape, identity, dtype=dtype)
x_axis_first = x.transpose(reaxis_first) x_axis_first = x.transpose(reaxis_first)
......
...@@ -1247,10 +1247,11 @@ def pydotprint( ...@@ -1247,10 +1247,11 @@ def pydotprint(
.. code-block:: python .. code-block:: python
import pytensor import pytensor
v = pytensor.tensor.vector() v = pytensor.tensor.vector()
from IPython.display import SVG from IPython.display import SVG
SVG(pytensor.printing.pydotprint(v*2, return_image=True,
format='svg')) SVG(pytensor.printing.pydotprint(v * 2, return_image=True, format="svg"))
In the graph, ellipses are Apply Nodes (the execution of an op) In the graph, ellipses are Apply Nodes (the execution of an op)
and boxes are variables. If variables have names they are used as and boxes are variables. If variables have names they are used as
......
...@@ -209,9 +209,9 @@ class autocast_float_as: ...@@ -209,9 +209,9 @@ class autocast_float_as:
Examples Examples
-------- --------
>>> from pytensor.tensor import fvector >>> from pytensor.tensor import fvector
>>> with autocast_float_as('float32'): >>> with autocast_float_as("float32"):
... assert (fvector() + 1.1).dtype == 'float32' # temporary downcasting ... assert (fvector() + 1.1).dtype == "float32" # temporary downcasting
>>> assert (fvector() + 1.1).dtype == 'float64' # back to default behaviour >>> assert (fvector() + 1.1).dtype == "float64" # back to default behaviour
""" """
......
...@@ -207,13 +207,20 @@ def scan( ...@@ -207,13 +207,20 @@ def scan(
.. code-block:: python .. code-block:: python
scan(fn, sequences = [ dict(input= Sequence1, taps = [-3,2,-1]) scan(
, Sequence2 fn,
, dict(input = Sequence3, taps = 3) ] sequences=[
, outputs_info = [ dict(initial = Output1, taps = [-3,-5]) dict(input=Sequence1, taps=[-3, 2, -1]),
, dict(initial = Output2, taps = None) Sequence2,
, Output3 ] dict(input=Sequence3, taps=3),
, non_sequences = [ Argument1, Argument2]) ],
outputs_info=[
dict(initial=Output1, taps=[-3, -5]),
dict(initial=Output2, taps=None),
Output3,
],
non_sequences=[Argument1, Argument2],
)
`fn` should expect the following arguments in this given order: `fn` should expect the following arguments in this given order:
...@@ -240,11 +247,12 @@ def scan( ...@@ -240,11 +247,12 @@ def scan(
import pytensor.tensor as pt import pytensor.tensor as pt
W = pt.matrix() W = pt.matrix()
W_2 = W**2 W_2 = W**2
def f(x): def f(x):
return pt.dot(x,W_2) return pt.dot(x, W_2)
The function `fn` is expected to return two things. One is a list of The function `fn` is expected to return two things. One is a list of
outputs ordered in the same order as `outputs_info`, with the outputs ordered in the same order as `outputs_info`, with the
...@@ -266,7 +274,7 @@ def scan( ...@@ -266,7 +274,7 @@ def scan(
.. code-block:: python .. code-block:: python
... ...
return [y1_t, y2_t], {x:x+1}, until(x < 50) return [y1_t, y2_t], {x: x + 1}, until(x < 50)
Note that a number of steps--considered in here as the maximum Note that a number of steps--considered in here as the maximum
number of steps--is still required even though a condition is number of steps--is still required even though a condition is
......
...@@ -1113,13 +1113,13 @@ def tril(m, k=0): ...@@ -1113,13 +1113,13 @@ def tril(m, k=0):
Examples Examples
-------- --------
>>> import pytensor.tensor as pt >>> import pytensor.tensor as pt
>>> pt.tril(pt.arange(1,13).reshape((4,3)), -1).eval() >>> pt.tril(pt.arange(1, 13).reshape((4, 3)), -1).eval()
array([[ 0, 0, 0], array([[ 0, 0, 0],
[ 4, 0, 0], [ 4, 0, 0],
[ 7, 8, 0], [ 7, 8, 0],
[10, 11, 12]]) [10, 11, 12]])
>>> pt.tril(pt.arange(3*4*5).reshape((3, 4, 5))).eval() >>> pt.tril(pt.arange(3 * 4 * 5).reshape((3, 4, 5))).eval()
array([[[ 0, 0, 0, 0, 0], array([[[ 0, 0, 0, 0, 0],
[ 5, 6, 0, 0, 0], [ 5, 6, 0, 0, 0],
[10, 11, 12, 0, 0], [10, 11, 12, 0, 0],
...@@ -1162,7 +1162,7 @@ def triu(m, k=0): ...@@ -1162,7 +1162,7 @@ def triu(m, k=0):
[ 0, 8, 9], [ 0, 8, 9],
[ 0, 0, 12]]) [ 0, 0, 12]])
>>> pt.triu(np.arange(3*4*5).reshape((3, 4, 5))).eval() >>> pt.triu(np.arange(3 * 4 * 5).reshape((3, 4, 5))).eval()
array([[[ 0, 1, 2, 3, 4], array([[[ 0, 1, 2, 3, 4],
[ 0, 6, 7, 8, 9], [ 0, 6, 7, 8, 9],
[ 0, 0, 12, 13, 14], [ 0, 0, 12, 13, 14],
...@@ -2108,9 +2108,9 @@ class Split(COp): ...@@ -2108,9 +2108,9 @@ class Split(COp):
>>> splits = pt.vector(dtype="int") >>> splits = pt.vector(dtype="int")
You have to declare right away how many split_points there will be. You have to declare right away how many split_points there will be.
>>> ra, rb, rc = pt.split(x, splits, n_splits = 3, axis = 0) >>> ra, rb, rc = pt.split(x, splits, n_splits=3, axis=0)
>>> f = function([x, splits], [ra, rb, rc]) >>> f = function([x, splits], [ra, rb, rc])
>>> a, b, c = f([0,1,2,3,4,5], [3, 2, 1]) >>> a, b, c = f([0, 1, 2, 3, 4, 5], [3, 2, 1])
>>> a >>> a
array([0, 1, 2]) array([0, 1, 2])
>>> b >>> b
...@@ -2831,28 +2831,28 @@ def stack(tensors: Sequence["TensorLike"], axis: int = 0): ...@@ -2831,28 +2831,28 @@ def stack(tensors: Sequence["TensorLike"], axis: int = 0):
>>> b = pytensor.tensor.type.scalar() >>> b = pytensor.tensor.type.scalar()
>>> c = pytensor.tensor.type.scalar() >>> c = pytensor.tensor.type.scalar()
>>> x = pytensor.tensor.stack([a, b, c]) >>> x = pytensor.tensor.stack([a, b, c])
>>> x.ndim # x is a vector of length 3. >>> x.ndim # x is a vector of length 3.
1 1
>>> a = pytensor.tensor.type.tensor4() >>> a = pytensor.tensor.type.tensor4()
>>> b = pytensor.tensor.type.tensor4() >>> b = pytensor.tensor.type.tensor4()
>>> c = pytensor.tensor.type.tensor4() >>> c = pytensor.tensor.type.tensor4()
>>> x = pytensor.tensor.stack([a, b, c]) >>> x = pytensor.tensor.stack([a, b, c])
>>> x.ndim # x is a 5d tensor. >>> x.ndim # x is a 5d tensor.
5 5
>>> rval = x.eval(dict((t, np.zeros((2, 2, 2, 2))) for t in [a, b, c])) >>> rval = x.eval(dict((t, np.zeros((2, 2, 2, 2))) for t in [a, b, c]))
>>> rval.shape # 3 tensors are stacked on axis 0 >>> rval.shape # 3 tensors are stacked on axis 0
(3, 2, 2, 2, 2) (3, 2, 2, 2, 2)
>>> x = pytensor.tensor.stack([a, b, c], axis=3) >>> x = pytensor.tensor.stack([a, b, c], axis=3)
>>> x.ndim >>> x.ndim
5 5
>>> rval = x.eval(dict((t, np.zeros((2, 2, 2, 2))) for t in [a, b, c])) >>> rval = x.eval(dict((t, np.zeros((2, 2, 2, 2))) for t in [a, b, c]))
>>> rval.shape # 3 tensors are stacked on axis 3 >>> rval.shape # 3 tensors are stacked on axis 3
(2, 2, 2, 3, 2) (2, 2, 2, 3, 2)
>>> x = pytensor.tensor.stack([a, b, c], axis=-2) >>> x = pytensor.tensor.stack([a, b, c], axis=-2)
>>> x.ndim >>> x.ndim
5 5
>>> rval = x.eval(dict((t, np.zeros((2, 2, 2, 2))) for t in [a, b, c])) >>> rval = x.eval(dict((t, np.zeros((2, 2, 2, 2))) for t in [a, b, c]))
>>> rval.shape # 3 tensors are stacked on axis -2 >>> rval.shape # 3 tensors are stacked on axis -2
(2, 2, 2, 3, 2) (2, 2, 2, 3, 2)
""" """
if not isinstance(tensors, Sequence): if not isinstance(tensors, Sequence):
...@@ -3892,7 +3892,7 @@ def stacklists(arg): ...@@ -3892,7 +3892,7 @@ def stacklists(arg):
>>> from pytensor.tensor import stacklists >>> from pytensor.tensor import stacklists
>>> from pytensor.tensor.type import scalars, matrices >>> from pytensor.tensor.type import scalars, matrices
>>> from pytensor import function >>> from pytensor import function
>>> a, b, c, d = scalars('abcd') >>> a, b, c, d = scalars("abcd")
>>> X = stacklists([[a, b], [c, d]]) >>> X = stacklists([[a, b], [c, d]])
>>> f = function([a, b, c, d], X) >>> f = function([a, b, c, d], X)
>>> f(1, 2, 3, 4) >>> f(1, 2, 3, 4)
...@@ -3903,10 +3903,10 @@ def stacklists(arg): ...@@ -3903,10 +3903,10 @@ def stacklists(arg):
a 2 by 2 grid: a 2 by 2 grid:
>>> from numpy import ones >>> from numpy import ones
>>> a, b, c, d = matrices('abcd') >>> a, b, c, d = matrices("abcd")
>>> X = stacklists([[a, b], [c, d]]) >>> X = stacklists([[a, b], [c, d]])
>>> f = function([a, b, c, d], X) >>> f = function([a, b, c, d], X)
>>> x = ones((4, 4), 'float32') >>> x = ones((4, 4), "float32")
>>> f(x, x, x, x).shape >>> f(x, x, x, x).shape
(2, 2, 4, 4) (2, 2, 4, 4)
......
...@@ -467,6 +467,7 @@ def einsum(subscripts: str, *operands: "TensorLike", optimize=None) -> TensorVar ...@@ -467,6 +467,7 @@ def einsum(subscripts: str, *operands: "TensorLike", optimize=None) -> TensorVar
.. code-block:: python .. code-block:: python
import pytensor as pt import pytensor as pt
A = pt.matrix("A") A = pt.matrix("A")
B = pt.matrix("B") B = pt.matrix("B")
C = pt.einsum("ij, jk -> ik", A, B) C = pt.einsum("ij, jk -> ik", A, B)
...@@ -481,6 +482,7 @@ def einsum(subscripts: str, *operands: "TensorLike", optimize=None) -> TensorVar ...@@ -481,6 +482,7 @@ def einsum(subscripts: str, *operands: "TensorLike", optimize=None) -> TensorVar
.. code-block:: python .. code-block:: python
import pytensor as pt import pytensor as pt
A = pt.tensor("A", shape=(None, 4, 5)) A = pt.tensor("A", shape=(None, 4, 5))
B = pt.tensor("B", shape=(None, 5, 6)) B = pt.tensor("B", shape=(None, 5, 6))
C = pt.einsum("bij, bjk -> bik", A, B) C = pt.einsum("bij, bjk -> bik", A, B)
...@@ -496,6 +498,7 @@ def einsum(subscripts: str, *operands: "TensorLike", optimize=None) -> TensorVar ...@@ -496,6 +498,7 @@ def einsum(subscripts: str, *operands: "TensorLike", optimize=None) -> TensorVar
.. code-block:: python .. code-block:: python
import pytensor as pt import pytensor as pt
A = pt.tensor("A", shape=(4, None, None, None, 5)) A = pt.tensor("A", shape=(4, None, None, None, 5))
B = pt.tensor("B", shape=(5, None, None, None, 6)) B = pt.tensor("B", shape=(5, None, None, None, 6))
C = pt.einsum("i...j, j...k -> ...ik", A, B) C = pt.einsum("i...j, j...k -> ...ik", A, B)
...@@ -510,6 +513,7 @@ def einsum(subscripts: str, *operands: "TensorLike", optimize=None) -> TensorVar ...@@ -510,6 +513,7 @@ def einsum(subscripts: str, *operands: "TensorLike", optimize=None) -> TensorVar
.. code-block:: python .. code-block:: python
import pytensor as pt import pytensor as pt
x = pt.tensor("x", shape=(3,)) x = pt.tensor("x", shape=(3,))
y = pt.tensor("y", shape=(4,)) y = pt.tensor("y", shape=(4,))
z = pt.einsum("i, j -> ij", x, y) z = pt.einsum("i, j -> ij", x, y)
......
...@@ -77,7 +77,7 @@ class DimShuffle(ExternalCOp): ...@@ -77,7 +77,7 @@ class DimShuffle(ExternalCOp):
.. code-block:: python .. code-block:: python
DimShuffle((False, False, False), ['x', 2, 'x', 0, 1]) DimShuffle((False, False, False), ["x", 2, "x", 0, 1])
This `Op` will only work on 3d tensors with no broadcastable This `Op` will only work on 3d tensors with no broadcastable
dimensions. The first dimension will be broadcastable, dimensions. The first dimension will be broadcastable,
...@@ -101,16 +101,16 @@ class DimShuffle(ExternalCOp): ...@@ -101,16 +101,16 @@ class DimShuffle(ExternalCOp):
-------- --------
.. code-block:: python .. code-block:: python
DimShuffle((), ['x']) # make a 0d (scalar) into a 1d vector DimShuffle((), ["x"]) # make a 0d (scalar) into a 1d vector
DimShuffle((False, False), [0, 1]) # identity DimShuffle((False, False), [0, 1]) # identity
DimShuffle((False, False), [1, 0]) # inverts the 1st and 2nd dimensions DimShuffle((False, False), [1, 0]) # inverts the 1st and 2nd dimensions
DimShuffle((False,), ['x', 0]) # make a row out of a 1d vector DimShuffle((False,), ["x", 0]) # make a row out of a 1d vector
# (N to 1xN) # (N to 1xN)
DimShuffle((False,), [0, 'x']) # make a column out of a 1d vector DimShuffle((False,), [0, "x"]) # make a column out of a 1d vector
# (N to Nx1) # (N to Nx1)
DimShuffle((False, False, False), [2, 0, 1]) # AxBxC to CxAxB DimShuffle((False, False, False), [2, 0, 1]) # AxBxC to CxAxB
DimShuffle((False, False), [0, 'x', 1]) # AxB to Ax1xB DimShuffle((False, False), [0, "x", 1]) # AxB to Ax1xB
DimShuffle((False, False), [1, 'x', 0]) # AxB to Bx1xA DimShuffle((False, False), [1, "x", 0]) # AxB to Bx1xA
The reordering of the dimensions can be done with the numpy.transpose The reordering of the dimensions can be done with the numpy.transpose
function. function.
...@@ -1180,15 +1180,15 @@ class CAReduce(COp): ...@@ -1180,15 +1180,15 @@ class CAReduce(COp):
----- -----
.. code-block:: python .. code-block:: python
CAReduce(add) # sum (ie, acts like the numpy sum operation) CAReduce(add) # sum (ie, acts like the numpy sum operation)
CAReduce(mul) # product CAReduce(mul) # product
CAReduce(maximum) # max CAReduce(maximum) # max
CAReduce(minimum) # min CAReduce(minimum) # min
CAReduce(or_) # any # not lazy CAReduce(or_) # any # not lazy
CAReduce(and_) # all # not lazy CAReduce(and_) # all # not lazy
CAReduce(xor) # a bit at 1 tell that there was an odd number of CAReduce(xor) # a bit at 1 tell that there was an odd number of
# bit at that position that where 1. 0 it was an # bit at that position that where 1. 0 it was an
# even number ... # even number ...
In order to (eventually) optimize memory usage patterns, In order to (eventually) optimize memory usage patterns,
`CAReduce` makes zero guarantees on the order in which it `CAReduce` makes zero guarantees on the order in which it
......
...@@ -267,13 +267,13 @@ def searchsorted(x, v, side="left", sorter=None): ...@@ -267,13 +267,13 @@ def searchsorted(x, v, side="left", sorter=None):
>>> from pytensor.tensor import extra_ops >>> from pytensor.tensor import extra_ops
>>> x = pt.dvector("x") >>> x = pt.dvector("x")
>>> idx = x.searchsorted(3) >>> idx = x.searchsorted(3)
>>> idx.eval({x: [1,2,3,4,5]}) >>> idx.eval({x: [1, 2, 3, 4, 5]})
array(2) array(2)
>>> extra_ops.searchsorted([1,2,3,4,5], 3).eval() >>> extra_ops.searchsorted([1, 2, 3, 4, 5], 3).eval()
array(2) array(2)
>>> extra_ops.searchsorted([1,2,3,4,5], 3, side='right').eval() >>> extra_ops.searchsorted([1, 2, 3, 4, 5], 3, side="right").eval()
array(3) array(3)
>>> extra_ops.searchsorted([1,2,3,4,5], [-10, 10, 2, 3]).eval() >>> extra_ops.searchsorted([1, 2, 3, 4, 5], [-10, 10, 2, 3]).eval()
array([0, 5, 1, 2]) array([0, 5, 1, 2])
.. versionadded:: 0.9 .. versionadded:: 0.9
...@@ -1176,7 +1176,7 @@ class Unique(Op): ...@@ -1176,7 +1176,7 @@ class Unique(Op):
>>> x = pytensor.tensor.vector() >>> x = pytensor.tensor.vector()
>>> f = pytensor.function([x], Unique(True, True, False)(x)) >>> f = pytensor.function([x], Unique(True, True, False)(x))
>>> f([1, 2., 3, 4, 3, 2, 1.]) >>> f([1, 2.0, 3, 4, 3, 2, 1.0])
[array([1., 2., 3., 4.]), array([0, 1, 2, 3]), array([0, 1, 2, 3, 2, 1, 0])] [array([1., 2., 3., 4.]), array([0, 1, 2, 3]), array([0, 1, 2, 3, 2, 1, 0])]
>>> y = pytensor.tensor.matrix() >>> y = pytensor.tensor.matrix()
......
...@@ -39,9 +39,11 @@ def vectorize(func: Callable, signature: str | None = None) -> Callable: ...@@ -39,9 +39,11 @@ def vectorize(func: Callable, signature: str | None = None) -> Callable:
import pytensor import pytensor
import pytensor.tensor as pt import pytensor.tensor as pt
def func(x): def func(x):
return pt.exp(x) / pt.sum(pt.exp(x)) return pt.exp(x) / pt.sum(pt.exp(x))
vec_func = pt.vectorize(func, signature="(a)->(a)") vec_func = pt.vectorize(func, signature="(a)->(a)")
x = pt.matrix("x") x = pt.matrix("x")
...@@ -58,9 +60,11 @@ def vectorize(func: Callable, signature: str | None = None) -> Callable: ...@@ -58,9 +60,11 @@ def vectorize(func: Callable, signature: str | None = None) -> Callable:
import pytensor import pytensor
import pytensor.tensor as pt import pytensor.tensor as pt
def func(x): def func(x):
return x[0], x[-1] return x[0], x[-1]
vec_func = pt.vectorize(func, signature="(a)->(),()") vec_func = pt.vectorize(func, signature="(a)->(),()")
x = pt.matrix("x") x = pt.matrix("x")
......
...@@ -80,8 +80,8 @@ def load(path, dtype, shape, mmap_mode=None): ...@@ -80,8 +80,8 @@ def load(path, dtype, shape, mmap_mode=None):
-------- --------
>>> from pytensor import * >>> from pytensor import *
>>> path = Variable(Generic(), None) >>> path = Variable(Generic(), None)
>>> x = tensor.load(path, 'int64', (None,)) >>> x = tensor.load(path, "int64", (None,))
>>> y = x*2 >>> y = x * 2
>>> fn = function([path], y) >>> fn = function([path], y)
>>> fn("stored-array.npy") # doctest: +SKIP >>> fn("stored-array.npy") # doctest: +SKIP
array([0, 2, 4, 6, 8], dtype=int64) array([0, 2, 4, 6, 8], dtype=int64)
......
...@@ -2098,27 +2098,27 @@ def tensordot( ...@@ -2098,27 +2098,27 @@ def tensordot(
are compatible. The resulting tensor will have shape (2, 5, 6) -- the are compatible. The resulting tensor will have shape (2, 5, 6) -- the
dimensions that are not being summed: dimensions that are not being summed:
>>> a = np.random.random((2,3,4)) >>> a = np.random.random((2, 3, 4))
>>> b = np.random.random((5,6,4,3)) >>> b = np.random.random((5, 6, 4, 3))
#tensordot #tensordot
>>> c = np.tensordot(a, b, [[1,2],[3,2]]) >>> c = np.tensordot(a, b, [[1, 2], [3, 2]])
#loop replicating tensordot #loop replicating tensordot
>>> a0, a1, a2 = a.shape >>> a0, a1, a2 = a.shape
>>> b0, b1, _, _ = b.shape >>> b0, b1, _, _ = b.shape
>>> cloop = np.zeros((a0,b0,b1)) >>> cloop = np.zeros((a0, b0, b1))
#loop over non-summed indices -- these exist #loop over non-summed indices -- these exist
#in the tensor product. #in the tensor product.
>>> for i in range(a0): >>> for i in range(a0):
... for j in range(b0): ... for j in range(b0):
... for k in range(b1): ... for k in range(b1):
... #loop over summed indices -- these don't exist ... # loop over summed indices -- these don't exist
... #in the tensor product. ... # in the tensor product.
... for l in range(a1): ... for l in range(a1):
... for m in range(a2): ... for m in range(a2):
... cloop[i,j,k] += a[i,l,m] * b[j,k,m,l] ... cloop[i, j, k] += a[i, l, m] * b[j, k, m, l]
>>> np.allclose(c, cloop) >>> np.allclose(c, cloop)
True True
......
...@@ -61,8 +61,9 @@ def shape_of_variables( ...@@ -61,8 +61,9 @@ def shape_of_variables(
-------- --------
>>> import pytensor.tensor as pt >>> import pytensor.tensor as pt
>>> from pytensor.graph.fg import FunctionGraph >>> from pytensor.graph.fg import FunctionGraph
>>> x = pt.matrix('x') >>> x = pt.matrix("x")
>>> y = x[512:]; y.name = 'y' >>> y = x[512:]
>>> y.name = "y"
>>> fgraph = FunctionGraph([x], [y], clone=False) >>> fgraph = FunctionGraph([x], [y], clone=False)
>>> d = shape_of_variables(fgraph, {x: (1024, 1024)}) >>> d = shape_of_variables(fgraph, {x: (1024, 1024)})
>>> d[y] >>> d[y]
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论