refactor: renamed LazyValueFunc to Func

main
Sofus Albert Høgsbro Rose 2024-05-21 09:04:05 +02:00
parent 84825c2642
commit dccf952ad3
Signed by: so-rose
GPG Key ID: AD901CB0F3701434
20 changed files with 141 additions and 151 deletions

View File

@ -48,7 +48,7 @@ from .flow_kinds import (
FlowKind, FlowKind,
InfoFlow, InfoFlow,
RangeFlow, RangeFlow,
LazyValueFuncFlow, FuncFlow,
ParamsFlow, ParamsFlow,
ScalingMode, ScalingMode,
ValueFlow, ValueFlow,
@ -119,7 +119,7 @@ __all__ = [
'FlowKind', 'FlowKind',
'InfoFlow', 'InfoFlow',
'RangeFlow', 'RangeFlow',
'LazyValueFuncFlow', 'FuncFlow',
'ParamsFlow', 'ParamsFlow',
'ScalingMode', 'ScalingMode',
'ValueFlow', 'ValueFlow',

View File

@ -19,7 +19,7 @@ from .capabilities import CapabilitiesFlow
from .flow_kinds import FlowKind from .flow_kinds import FlowKind
from .info import InfoFlow from .info import InfoFlow
from .lazy_range import RangeFlow, ScalingMode from .lazy_range import RangeFlow, ScalingMode
from .lazy_value_func import LazyValueFuncFlow from .lazy_func import FuncFlow
from .params import ParamsFlow from .params import ParamsFlow
from .value import ValueFlow from .value import ValueFlow
@ -30,7 +30,7 @@ __all__ = [
'InfoFlow', 'InfoFlow',
'RangeFlow', 'RangeFlow',
'ScalingMode', 'ScalingMode',
'LazyValueFuncFlow', 'FuncFlow',
'ParamsFlow', 'ParamsFlow',
'ValueFlow', 'ValueFlow',
] ]

View File

@ -40,7 +40,7 @@ class FlowKind(enum.StrEnum):
Array: An object with dimensions, and possibly a unit. Array: An object with dimensions, and possibly a unit.
Whenever a `Value` is defined, a single-element `list` will also be generated by default as `Array` Whenever a `Value` is defined, a single-element `list` will also be generated by default as `Array`
However, for any other array-like variants (or sockets that only represent array-like objects), `Array` should be defined manually. However, for any other array-like variants (or sockets that only represent array-like objects), `Array` should be defined manually.
LazyValueFunc: A composable function. Func: A composable function.
Can be used to represent computations for which all data is not yet known, or for which just-in-time compilation can drastically increase performance. Can be used to represent computations for which all data is not yet known, or for which just-in-time compilation can drastically increase performance.
Range: An object that generates an `Array` from range information (start/stop/step/spacing). Range: An object that generates an `Array` from range information (start/stop/step/spacing).
This should be used instead of `Array` whenever possible. This should be used instead of `Array` whenever possible.
@ -55,7 +55,7 @@ class FlowKind(enum.StrEnum):
Array = enum.auto() Array = enum.auto()
# Lazy # Lazy
LazyValueFunc = enum.auto() Func = enum.auto()
Range = enum.auto() Range = enum.auto()
# Auxiliary # Auxiliary
@ -96,7 +96,7 @@ class FlowKind(enum.StrEnum):
return { return {
FlowKind.Value: FlowKind.Value, FlowKind.Value: FlowKind.Value,
FlowKind.Array: FlowKind.Array, FlowKind.Array: FlowKind.Array,
FlowKind.LazyValueFunc: FlowKind.LazyValueFunc, FlowKind.Func: FlowKind.Func,
FlowKind.Range: FlowKind.Range, FlowKind.Range: FlowKind.Range,
}[self] }[self]
@ -106,7 +106,7 @@ class FlowKind(enum.StrEnum):
FlowKind.Value: 'CIRCLE', FlowKind.Value: 'CIRCLE',
FlowKind.Array: 'SQUARE', FlowKind.Array: 'SQUARE',
FlowKind.Range: 'SQUARE', FlowKind.Range: 'SQUARE',
FlowKind.LazyValueFunc: 'DIAMOND', FlowKind.Func: 'DIAMOND',
}[self] }[self]
#################### ####################
@ -119,7 +119,7 @@ class FlowKind(enum.StrEnum):
FlowKind.Value: 'Value', FlowKind.Value: 'Value',
FlowKind.Array: 'Array', FlowKind.Array: 'Array',
FlowKind.Range: 'Range', FlowKind.Range: 'Range',
FlowKind.LazyValueFunc: 'Func', FlowKind.Func: 'Func',
FlowKind.Params: 'Parameters', FlowKind.Params: 'Parameters',
FlowKind.Info: 'Information', FlowKind.Info: 'Information',
}[v] }[v]

View File

@ -30,7 +30,7 @@ LazyFunction: typ.TypeAlias = typ.Callable[[typ.Any, ...], typ.Any]
@dataclasses.dataclass(frozen=True, kw_only=True) @dataclasses.dataclass(frozen=True, kw_only=True)
class LazyValueFuncFlow: class FuncFlow:
r"""Defines a flow of data as incremental function composition. r"""Defines a flow of data as incremental function composition.
For specific math system usage instructions, please consult the documentation of relevant nodes. For specific math system usage instructions, please consult the documentation of relevant nodes.
@ -44,9 +44,9 @@ class LazyValueFuncFlow:
- **Performant**: Since no operations are happening, the UI feels fast and snappy. - **Performant**: Since no operations are happening, the UI feels fast and snappy.
## Strongly Related FlowKinds ## Strongly Related FlowKinds
For doing math, `LazyValueFunc` relies on two other `FlowKind`s, which must run in parallel: For doing math, `Func` relies on two other `FlowKind`s, which must run in parallel:
- `FlowKind.Info`: Tracks the name, `spux.MathType`, unit (if any), length, and index coordinates for the raw data object produced by `LazyValueFunc`. - `FlowKind.Info`: Tracks the name, `spux.MathType`, unit (if any), length, and index coordinates for the raw data object produced by `Func`.
- `FlowKind.Params`: Tracks the particular values of input parameters to the lazy function, each of which can also be symbolic. - `FlowKind.Params`: Tracks the particular values of input parameters to the lazy function, each of which can also be symbolic.
For more, please see the documentation for each. For more, please see the documentation for each.
@ -84,12 +84,12 @@ class LazyValueFuncFlow:
## 'A0', 'KV0' are of length 'p' and 'q' ## 'A0', 'KV0' are of length 'p' and 'q'
def f_0(*args, **kwargs): ... def f_0(*args, **kwargs): ...
lazy_value_func_0 = LazyValueFuncFlow( lazy_func_0 = FuncFlow(
func=f_0, func=f_0,
func_args=[(a_i, type(a_i)) for a_i in A0], func_args=[(a_i, type(a_i)) for a_i in A0],
func_kwargs={k: v for k,v in KV0}, func_kwargs={k: v for k,v in KV0},
) )
output_0 = lazy_value_func.func(*A0_computed, **KV0_computed) output_0 = lazy_func.func(*A0_computed, **KV0_computed)
``` ```
## `depth>0`: Composition Chaining ## `depth>0`: Composition Chaining
@ -120,7 +120,7 @@ class LazyValueFuncFlow:
## 'A1', 'KV1' are therefore of length 'r' and 's' ## 'A1', 'KV1' are therefore of length 'r' and 's'
def f_1(output_0, *args, **kwargs): ... def f_1(output_0, *args, **kwargs): ...
lazy_value_func_1 = lazy_value_func_0.compose_within( lazy_func_1 = lazy_func_0.compose_within(
enclosing_func=f_1, enclosing_func=f_1,
enclosing_func_args=[(a_i, type(a_i)) for a_i in A1], enclosing_func_args=[(a_i, type(a_i)) for a_i in A1],
enclosing_func_kwargs={k: type(v) for k,v in K1}, enclosing_func_kwargs={k: type(v) for k,v in K1},
@ -128,10 +128,10 @@ class LazyValueFuncFlow:
A_computed = A0_computed + A1_computed A_computed = A0_computed + A1_computed
KW_computed = KV0_computed + KV1_computed KW_computed = KV0_computed + KV1_computed
output_1 = lazy_value_func_1.func(*A_computed, **KW_computed) output_1 = lazy_func_1.func(*A_computed, **KW_computed)
``` ```
By using `LazyValueFunc`, we've guaranteed that even hugely deep $n$s won't ever look more complicated than this. By using `Func`, we've guaranteed that even hugely deep $n$s won't ever look more complicated than this.
## `max depth`: "Realization" ## `max depth`: "Realization"
So, we've composed a bunch of functions of functions of ... So, we've composed a bunch of functions of functions of ...
@ -142,22 +142,22 @@ class LazyValueFuncFlow:
```python ```python
# A_all and KW_all must be tracked on the side. # A_all and KW_all must be tracked on the side.
output_n = lazy_value_func_n.func(*A_all, **KW_all) output_n = lazy_func_n.func(*A_all, **KW_all)
``` ```
Of course, this comes with enormous overhead. Of course, this comes with enormous overhead.
Aside from the function calls themselves (which can be non-trivial), we must also contend with the enormous inefficiency of performing array operations sequentially. Aside from the function calls themselves (which can be non-trivial), we must also contend with the enormous inefficiency of performing array operations sequentially.
That brings us to the killer feature of `LazyValueFuncFlow`, and the motivating reason for doing any of this at all: That brings us to the killer feature of `FuncFlow`, and the motivating reason for doing any of this at all:
```python ```python
output_n = lazy_value_func_n.func_jax(*A_all, **KW_all) output_n = lazy_func_n.func_jax(*A_all, **KW_all)
``` ```
What happened was, **the entire pipeline** was compiled, optimized, and computed with bare-metal performance on either a CPU, GPU, or TPU. What happened was, **the entire pipeline** was compiled, optimized, and computed with bare-metal performance on either a CPU, GPU, or TPU.
With the help of the `jax` library (and its underlying OpenXLA bytecode), all of that inefficiency has been optimized based on _what we're trying to do_, not _exactly how we're doing it_, in order to maximize the use of modern massively-parallel devices. With the help of the `jax` library (and its underlying OpenXLA bytecode), all of that inefficiency has been optimized based on _what we're trying to do_, not _exactly how we're doing it_, in order to maximize the use of modern massively-parallel devices.
See the documentation of `LazyValueFunc.func_jax()` for more information on this process. See the documentation of `Func.func_jax()` for more information on this process.
@ -187,12 +187,12 @@ class LazyValueFuncFlow:
As a more nuanced example, when lag occurs due to the computing an image-based plot based on live-computed math, then the visual feedback of _the plot actually changing_ seems to have a similar effect, not least because it's emotionally well-understood that detaching the `Viewer` node would also remove the lag. As a more nuanced example, when lag occurs due to the computing an image-based plot based on live-computed math, then the visual feedback of _the plot actually changing_ seems to have a similar effect, not least because it's emotionally well-understood that detaching the `Viewer` node would also remove the lag.
In short: Even if lazy evaluation didn't make any math faster, it will still _feel_ faster (to a point - raw performance obviously still matters). In short: Even if lazy evaluation didn't make any math faster, it will still _feel_ faster (to a point - raw performance obviously still matters).
Without `LazyValueFuncFlow`, the point of evaluation cannot be chosen at all, which is a huge issue for all the named reasons. Without `FuncFlow`, the point of evaluation cannot be chosen at all, which is a huge issue for all the named reasons.
With `LazyValueFuncFlow`, better-chosen evaluation points can be chosen to cause the _user experience_ of high performance, simply because we were able to shift the exact same computation to a point in time where the user either understands or tolerates the delay better. With `FuncFlow`, better-chosen evaluation points can be chosen to cause the _user experience_ of high performance, simply because we were able to shift the exact same computation to a point in time where the user either understands or tolerates the delay better.
## Flexibility ## Flexibility
Large-scale math is done on tensors, whether one knows (or likes!) it or not. Large-scale math is done on tensors, whether one knows (or likes!) it or not.
To this end, the indexed arrays produced by `LazyValueFuncFlow.func_jax` aren't quite sufficient for most operations we want to do: To this end, the indexed arrays produced by `FuncFlow.func_jax` aren't quite sufficient for most operations we want to do:
- **Naming**: What _is_ each axis? - **Naming**: What _is_ each axis?
Unnamed index axes are sometimes easy to decode, but in general, names have an unexpectedly critical function when operating on arrays. Unnamed index axes are sometimes easy to decode, but in general, names have an unexpectedly critical function when operating on arrays.
@ -206,13 +206,13 @@ class LazyValueFuncFlow:
Not only do we endeavor to track these, but we also introduce unit-awareness to the coordinates, and design the entire math system to visually communicate the state of arrays before/after every single computation, as well as only expose operations that this tracked data indicates possible. Not only do we endeavor to track these, but we also introduce unit-awareness to the coordinates, and design the entire math system to visually communicate the state of arrays before/after every single computation, as well as only expose operations that this tracked data indicates possible.
In practice, this happens in `FlowKind.Info`, which due to having its own `FlowKind` "lane" can be adjusted without triggering changes to (and therefore recompilation of) the `FlowKind.LazyValueFunc` chain. In practice, this happens in `FlowKind.Info`, which due to having its own `FlowKind` "lane" can be adjusted without triggering changes to (and therefore recompilation of) the `FlowKind.Func` chain.
**Please consult the `InfoFlow` documentation for more**. **Please consult the `InfoFlow` documentation for more**.
## Performance ## Performance
All values introduced while processing are kept in a seperate `FlowKind` lane, with its own incremental caching: `FlowKind.Params`. All values introduced while processing are kept in a seperate `FlowKind` lane, with its own incremental caching: `FlowKind.Params`.
It's a simple mechanism, but for the cost of introducing an extra `FlowKind` "lane", all of the values used to process data can be live-adjusted without the overhead of recompiling the entire `LazyValueFunc` every time anything changes. It's a simple mechanism, but for the cost of introducing an extra `FlowKind` "lane", all of the values used to process data can be live-adjusted without the overhead of recompiling the entire `Func` every time anything changes.
Moreover, values used to process data don't even have to be numbers yet: They can be expressions of symbolic variables, complete with units, which are only realized at the very end of the chain, by the node that absolutely cannot function without the actual numerical data. Moreover, values used to process data don't even have to be numbers yet: They can be expressions of symbolic variables, complete with units, which are only realized at the very end of the chain, by the node that absolutely cannot function without the actual numerical data.
See the `ParamFlow` documentation for more information. See the `ParamFlow` documentation for more information.
@ -224,7 +224,7 @@ class LazyValueFuncFlow:
A few teasers of what nodes can do with this system: A few teasers of what nodes can do with this system:
**Auto-Differentiation**: `jax.jit` isn't even really the killer feature of `jax`. **Auto-Differentiation**: `jax.jit` isn't even really the killer feature of `jax`.
`jax` can automatically differentiate `LazyValueFuncFlow.func_jax` with respect to any input parameter, including for fwd/bck jacobians/hessians, with robust numerical stability. `jax` can automatically differentiate `FuncFlow.func_jax` with respect to any input parameter, including for fwd/bck jacobians/hessians, with robust numerical stability.
When used in When used in
**Symbolic Interop**: Any `sympy` expression containing symbolic variables can be compiled, by `sympy`, into a `jax`-compatible function which takes **Symbolic Interop**: Any `sympy` expression containing symbolic variables can be compiled, by `sympy`, into a `jax`-compatible function which takes
We make use of this in the `Expr` socket, enabling true symbolic math to be used in high-performance lazy `jax` computations. We make use of this in the `Expr` socket, enabling true symbolic math to be used in high-performance lazy `jax` computations.
@ -304,7 +304,7 @@ class LazyValueFuncFlow:
if self.supports_jax: if self.supports_jax:
return jax.jit(self.func) return jax.jit(self.func)
msg = 'Can\'t express LazyValueFuncFlow as JAX function (using jax.jit), since "self.supports_jax" is False' msg = 'Can\'t express FuncFlow as JAX function (using jax.jit), since "self.supports_jax" is False'
raise ValueError(msg) raise ValueError(msg)
#################### ####################
@ -317,7 +317,7 @@ class LazyValueFuncFlow:
enclosing_func_kwargs: dict[str, type] = MappingProxyType({}), enclosing_func_kwargs: dict[str, type] = MappingProxyType({}),
supports_jax: bool = False, supports_jax: bool = False,
) -> typ.Self: ) -> typ.Self:
"""Compose `self.func` within the given enclosing function, which itself takes arguments, and create a new `LazyValueFuncFlow` to contain it. """Compose `self.func` within the given enclosing function, which itself takes arguments, and create a new `FuncFlow` to contain it.
This is the fundamental operation used to "chain" functions together. This is the fundamental operation used to "chain" functions together.
@ -328,13 +328,13 @@ class LazyValueFuncFlow:
C = spux.MathType.Complex C = spux.MathType.Complex
x, y = sp.symbols('x y', real=True) x, y = sp.symbols('x y', real=True)
# Prepare "Root" LazyValueFuncFlow w/x,y args # Prepare "Root" FuncFlow w/x,y args
expr_root = 3*x + y**2 - 100 expr_root = 3*x + y**2 - 100
expr_root_func = sp.lambdify([x, y], expr, 'jax') expr_root_func = sp.lambdify([x, y], expr, 'jax')
func_root = LazyValueFuncFlow(func=expr_root_func, func_args=[R,R], supports_jax=True) func_root = FuncFlow(func=expr_root_func, func_args=[R,R], supports_jax=True)
# Compose "Enclosing" LazyValueFuncFlow w/z arg # Compose "Enclosing" FuncFlow w/z arg
r = sp.Symbol('z', real=True) r = sp.Symbol('z', real=True)
z = sp.Symbol('z', complex=True) z = sp.Symbol('z', complex=True)
expr = 10*sp.re(z) / (z + r) expr = 10*sp.re(z) / (z + r)
@ -351,7 +351,7 @@ class LazyValueFuncFlow:
Returns: Returns:
A lazy function that takes both the enclosed and enclosing arguments, and returns the value of the enclosing function (whose first argument is the output value of the enclosed function). A lazy function that takes both the enclosed and enclosing arguments, and returns the value of the enclosing function (whose first argument is the output value of the enclosed function).
""" """
return LazyValueFuncFlow( return FuncFlow(
func=lambda *args, **kwargs: enclosing_func( func=lambda *args, **kwargs: enclosing_func(
self.func( self.func(
*list(args[: len(self.func_args)]), *list(args[: len(self.func_args)]),
@ -373,17 +373,17 @@ class LazyValueFuncFlow:
Generally, `self.func` produces a single array as output (when doing math, at least). Generally, `self.func` produces a single array as output (when doing math, at least).
But sometimes (as in the `OperateMathNode`), we need to perform a binary operation between two arrays, like say, $+$. But sometimes (as in the `OperateMathNode`), we need to perform a binary operation between two arrays, like say, $+$.
Without realizing both `LazyValueFuncFlow`s, it's not immediately obvious how one might accomplish this. Without realizing both `FuncFlow`s, it's not immediately obvious how one might accomplish this.
This overloaded function of the `|` operator (used as `left | right`) solves that problem. This overloaded function of the `|` operator (used as `left | right`) solves that problem.
A new `LazyValueFuncFlow` is created, which takes the arguments of both inputs, and which produces a single output value: A 2-tuple, where each element if the output of each function. A new `FuncFlow` is created, which takes the arguments of both inputs, and which produces a single output value: A 2-tuple, where each element if the output of each function.
Examples: Examples:
Consider this illustrative (pseudocode) example: Consider this illustrative (pseudocode) example:
```python ```python
# Presume a,b are values, and that A,B are their identifiers. # Presume a,b are values, and that A,B are their identifiers.
func_1 = LazyValueFuncFlow(func=compute_big_data_1, func_args=[A]) func_1 = FuncFlow(func=compute_big_data_1, func_args=[A])
func_2 = LazyValueFuncFlow(func=compute_big_data_2, func_args=[B]) func_2 = FuncFlow(func=compute_big_data_2, func_args=[B])
f = (func_1 | func_2).compose_within(func=lambda D: D[0] + D[1]) f = (func_1 | func_2).compose_within(func=lambda D: D[0] + D[1])
@ -402,7 +402,7 @@ class LazyValueFuncFlow:
Returns: Returns:
A lazy function that takes all arguments of both inputs, and returns a 2-tuple containing both output arguments. A lazy function that takes all arguments of both inputs, and returns a 2-tuple containing both output arguments.
""" """
return LazyValueFuncFlow( return FuncFlow(
func=lambda *args, **kwargs: ( func=lambda *args, **kwargs: (
self.func( self.func(
*list(args[: len(self.func_args)]), *list(args[: len(self.func_args)]),

View File

@ -29,7 +29,7 @@ from blender_maxwell.utils import logger
from .array import ArrayFlow from .array import ArrayFlow
from .flow_kinds import FlowKind from .flow_kinds import FlowKind
from .lazy_value_func import LazyValueFuncFlow from .lazy_func import FuncFlow
log = logger.get(__name__) log = logger.get(__name__)
@ -361,7 +361,7 @@ class RangeFlow:
The ordering of the symbols is identical to `self.symbols`, which is guaranteed to be a deterministically sorted list of symbols. The ordering of the symbols is identical to `self.symbols`, which is guaranteed to be a deterministically sorted list of symbols.
Returns: Returns:
A `LazyValueFuncFlow` that, given the input symbols defined in `self.symbols`, A `FuncFlow` that, given the input symbols defined in `self.symbols`,
""" """
# Compile JAX Functions for Start/End Expressions # Compile JAX Functions for Start/End Expressions
## FYI, JAX-in-JAX works perfectly fine. ## FYI, JAX-in-JAX works perfectly fine.
@ -378,18 +378,18 @@ class RangeFlow:
return gen_array return gen_array
@functools.cached_property @functools.cached_property
def as_lazy_value_func(self) -> LazyValueFuncFlow: def as_lazy_func(self) -> FuncFlow:
"""Creates a `LazyValueFuncFlow` using the output of `self.as_func`. """Creates a `FuncFlow` using the output of `self.as_func`.
This is useful for ex. parameterizing the first array in the node graph, without binding an entire computed array. This is useful for ex. parameterizing the first array in the node graph, without binding an entire computed array.
Notes: Notes:
The the function enclosed in the `LazyValueFuncFlow` is identical to the one returned by `self.as_func`. The the function enclosed in the `FuncFlow` is identical to the one returned by `self.as_func`.
Returns: Returns:
A `LazyValueFuncFlow` containing `self.as_func`, as well as appropriate supporting settings. A `FuncFlow` containing `self.as_func`, as well as appropriate supporting settings.
""" """
return LazyValueFuncFlow( return FuncFlow(
func=self.as_func, func=self.as_func,
func_args=[(spux.MathType.from_expr(sym)) for sym in self.symbols], func_args=[(spux.MathType.from_expr(sym)) for sym in self.symbols],
supports_jax=True, supports_jax=True,
@ -401,7 +401,7 @@ class RangeFlow:
def realize_start( def realize_start(
self, self,
symbol_values: dict[spux.Symbol, typ.Any] = MappingProxyType({}), symbol_values: dict[spux.Symbol, typ.Any] = MappingProxyType({}),
) -> ArrayFlow | LazyValueFuncFlow: ) -> ArrayFlow | FuncFlow:
return spux.sympy_to_python( return spux.sympy_to_python(
self.start.subs({sym: symbol_values[sym.name] for sym in self.symbols}) self.start.subs({sym: symbol_values[sym.name] for sym in self.symbols})
) )
@ -409,7 +409,7 @@ class RangeFlow:
def realize_stop( def realize_stop(
self, self,
symbol_values: dict[spux.Symbol, typ.Any] = MappingProxyType({}), symbol_values: dict[spux.Symbol, typ.Any] = MappingProxyType({}),
) -> ArrayFlow | LazyValueFuncFlow: ) -> ArrayFlow | FuncFlow:
return spux.sympy_to_python( return spux.sympy_to_python(
self.stop.subs({sym: symbol_values[sym.name] for sym in self.symbols}) self.stop.subs({sym: symbol_values[sym.name] for sym in self.symbols})
) )
@ -417,7 +417,7 @@ class RangeFlow:
def realize_step_size( def realize_step_size(
self, self,
symbol_values: dict[spux.Symbol, typ.Any] = MappingProxyType({}), symbol_values: dict[spux.Symbol, typ.Any] = MappingProxyType({}),
) -> ArrayFlow | LazyValueFuncFlow: ) -> ArrayFlow | FuncFlow:
raw_step_size = (self.realize_stop() - self.realize_start() + 1) / self.steps raw_step_size = (self.realize_stop() - self.realize_start() + 1) / self.steps
if self.mathtype is spux.MathType.Integer and raw_step_size.is_integer(): if self.mathtype is spux.MathType.Integer and raw_step_size.is_integer():
@ -427,8 +427,8 @@ class RangeFlow:
def realize( def realize(
self, self,
symbol_values: dict[spux.Symbol, typ.Any] = MappingProxyType({}), symbol_values: dict[spux.Symbol, typ.Any] = MappingProxyType({}),
kind: typ.Literal[FlowKind.Array, FlowKind.LazyValueFunc] = FlowKind.Array, kind: typ.Literal[FlowKind.Array, FlowKind.Func] = FlowKind.Array,
) -> ArrayFlow | LazyValueFuncFlow: ) -> ArrayFlow | FuncFlow:
"""Apply a function to the bounds, effectively rescaling the represented array. """Apply a function to the bounds, effectively rescaling the represented array.
Notes: Notes:
@ -458,8 +458,8 @@ class RangeFlow:
if kind == FlowKind.Array: if kind == FlowKind.Array:
return ArrayFlow(values=gen_array(), unit=self.unit, is_sorted=True) return ArrayFlow(values=gen_array(), unit=self.unit, is_sorted=True)
if kind == FlowKind.LazyValueFunc: if kind == FlowKind.Func:
return LazyValueFuncFlow(func=gen_array, supports_jax=True) return FuncFlow(func=gen_array, supports_jax=True)
msg = f'Invalid kind: {kind}' msg = f'Invalid kind: {kind}'
raise TypeError(msg) raise TypeError(msg)

View File

@ -69,7 +69,7 @@ class ParamsFlow:
unit_system: spux.UnitSystem, unit_system: spux.UnitSystem,
symbol_values: dict[spux.Symbol, spux.SympyExpr] = MappingProxyType({}), symbol_values: dict[spux.Symbol, spux.SympyExpr] = MappingProxyType({}),
): ):
"""Realize the function arguments contained in this `ParamsFlow`, making it ready for insertion into `LazyValueFunc.func()`. """Realize the function arguments contained in this `ParamsFlow`, making it ready for insertion into `Func.func()`.
For all `arg`s in `self.func_args`, the following operations are performed: For all `arg`s in `self.func_args`, the following operations are performed:
- **Unit System**: If `arg` - **Unit System**: If `arg`
@ -121,7 +121,7 @@ class ParamsFlow:
): ):
"""Combine two function parameter lists, such that the LHS will be concatenated with the RHS. """Combine two function parameter lists, such that the LHS will be concatenated with the RHS.
Just like its neighbor in `LazyValueFunc`, this effectively combines two functions with unique parameters. Just like its neighbor in `Func`, this effectively combines two functions with unique parameters.
The next composed function will receive a tuple of two arrays, instead of just one, allowing binary operations to occur. The next composed function will receive a tuple of two arrays, instead of just one, allowing binary operations to occur.
""" """
return ParamsFlow( return ParamsFlow(

View File

@ -318,12 +318,12 @@ class DataFileFormat(enum.StrEnum):
"""Abstraction of a data file format, providing a regularized way of interacting with filesystem data. """Abstraction of a data file format, providing a regularized way of interacting with filesystem data.
Import/export interacts closely with the `Expr` socket's `FlowKind` semantics: Import/export interacts closely with the `Expr` socket's `FlowKind` semantics:
- `FlowKind.LazyValueFunc`: Generally realized on-import/export. - `FlowKind.Func`: Generally realized on-import/export.
- **Import**: Loading data is generally eager, but memory-mapped file loading would be manageable using this interface. - **Import**: Loading data is generally eager, but memory-mapped file loading would be manageable using this interface.
- **Export**: The function is realized and only the array is inserted into the file. - **Export**: The function is realized and only the array is inserted into the file.
- `FlowKind.Params`: Generally consumed. - `FlowKind.Params`: Generally consumed.
- **Import**: A new, empty `ParamsFlow` object is created. - **Import**: A new, empty `ParamsFlow` object is created.
- **Export**: The `ParamsFlow` is consumed when realizing the `LazyValueFunc`. - **Export**: The `ParamsFlow` is consumed when realizing the `Func`.
- `FlowKind.Info`: As the most important element, it is kept in an (optional) sidecar metadata file. - `FlowKind.Info`: As the most important element, it is kept in an (optional) sidecar metadata file.
- **Import**: The sidecar file is loaded, checked, and used, if it exists. A warning about further processing may show if it doesn't. - **Import**: The sidecar file is loaded, checked, and used, if it exists. A warning about further processing may show if it doesn't.
- **Export**: The sidecar file is written next to the canonical data file, in such a manner that it can be both read and loaded. - **Export**: The sidecar file is written next to the canonical data file, in such a manner that it can be both read and loaded.

View File

@ -57,9 +57,7 @@ class ExtractDataNode(base.MaxwellSimNode):
} }
output_socket_sets: typ.ClassVar = { output_socket_sets: typ.ClassVar = {
'Sim Data': {'Monitor Data': sockets.MaxwellMonitorDataSocketDef()}, 'Sim Data': {'Monitor Data': sockets.MaxwellMonitorDataSocketDef()},
'Monitor Data': { 'Monitor Data': {'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func)},
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc)
},
} }
#################### ####################
@ -349,7 +347,7 @@ class ExtractDataNode(base.MaxwellSimNode):
return ct.FlowSignal.FlowPending return ct.FlowSignal.FlowPending
#################### ####################
# - FlowKind.Array|LazyValueFunc: Monitor Data -> Expr # - FlowKind.Array|Func: Monitor Data -> Expr
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
@ -384,16 +382,14 @@ class ExtractDataNode(base.MaxwellSimNode):
@events.computes_output_socket( @events.computes_output_socket(
# Trigger # Trigger
'Expr', 'Expr',
kind=ct.FlowKind.LazyValueFunc, kind=ct.FlowKind.Func,
# Loaded # Loaded
output_sockets={'Expr'}, output_sockets={'Expr'},
output_socket_kinds={'Expr': ct.FlowKind.Array}, output_socket_kinds={'Expr': ct.FlowKind.Array},
output_sockets_optional={'Expr': True}, output_sockets_optional={'Expr': True},
) )
def compute_extracted_data_lazy( def compute_extracted_data_lazy(self, output_sockets: dict) -> ct.FuncFlow | None:
self, output_sockets: dict """Declare `Expr:Func` by creating a simple function that directly wraps `Expr:Array`.
) -> ct.LazyValueFuncFlow | None:
"""Declare `Expr:LazyValueFunc` by creating a simple function that directly wraps `Expr:Array`.
Returns: Returns:
The composable function array, if available, else `ct.FlowSignal.FlowPending`. The composable function array, if available, else `ct.FlowSignal.FlowPending`.
@ -402,9 +398,7 @@ class ExtractDataNode(base.MaxwellSimNode):
has_output_expr = not ct.FlowSignal.check(output_expr) has_output_expr = not ct.FlowSignal.check(output_expr)
if has_output_expr: if has_output_expr:
return ct.LazyValueFuncFlow( return ct.FuncFlow(func=lambda: output_expr.values, supports_jax=True)
func=lambda: output_expr.values, supports_jax=True
)
return ct.FlowSignal.FlowPending return ct.FlowSignal.FlowPending
@ -441,7 +435,7 @@ class ExtractDataNode(base.MaxwellSimNode):
"""Declare `Data:Info` by manually selecting appropriate axes, units, etc. for each monitor type. """Declare `Data:Info` by manually selecting appropriate axes, units, etc. for each monitor type.
Returns: Returns:
Information describing the `Data:LazyValueFunc`, if available, else `ct.FlowSignal.FlowPending`. Information describing the `Data:Func`, if available, else `ct.FlowSignal.FlowPending`.
""" """
monitor_data = input_sockets['Monitor Data'] monitor_data = input_sockets['Monitor Data']
monitor_data_type = props['monitor_data_type'] monitor_data_type = props['monitor_data_type']

View File

@ -265,10 +265,10 @@ class FilterMathNode(base.MaxwellSimNode):
bl_label = 'Filter Math' bl_label = 'Filter Math'
input_sockets: typ.ClassVar = { input_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
} }
output_sockets: typ.ClassVar = { output_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
} }
#################### ####################
@ -518,13 +518,13 @@ class FilterMathNode(base.MaxwellSimNode):
current_bl_socket = self.loose_input_sockets.get('Dim') current_bl_socket = self.loose_input_sockets.get('Dim')
if ( if (
current_bl_socket is None current_bl_socket is None
or current_bl_socket.active_kind != ct.FlowKind.LazyValueFunc or current_bl_socket.active_kind != ct.FlowKind.Func
or current_bl_socket.mathtype != spux.MathType.Real or current_bl_socket.mathtype != spux.MathType.Real
or current_bl_socket.physical_type != spux.PhysicalType.NonPhysical or current_bl_socket.physical_type != spux.PhysicalType.NonPhysical
): ):
self.loose_input_sockets = { self.loose_input_sockets = {
'Dim': sockets.ExprSocketDef( 'Dim': sockets.ExprSocketDef(
active_kind=ct.FlowKind.LazyValueFunc, active_kind=ct.FlowKind.Func,
mathtype=spux.MathType.Real, mathtype=spux.MathType.Real,
physical_type=spux.PhysicalType.NonPhysical, physical_type=spux.PhysicalType.NonPhysical,
show_info_columns=True, show_info_columns=True,
@ -536,28 +536,28 @@ class FilterMathNode(base.MaxwellSimNode):
self.loose_input_sockets = {} self.loose_input_sockets = {}
#################### ####################
# - FlowKind.Value|LazyValueFunc # - FlowKind.Value|Func
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
kind=ct.FlowKind.LazyValueFunc, kind=ct.FlowKind.Func,
props={'operation', 'dim_0', 'dim_1', 'slice_tuple'}, props={'operation', 'dim_0', 'dim_1', 'slice_tuple'},
input_sockets={'Expr'}, input_sockets={'Expr'},
input_socket_kinds={'Expr': {ct.FlowKind.LazyValueFunc, ct.FlowKind.Info}}, input_socket_kinds={'Expr': {ct.FlowKind.Func, ct.FlowKind.Info}},
) )
def compute_lazy_value_func(self, props: dict, input_sockets: dict): def compute_lazy_func(self, props: dict, input_sockets: dict):
operation = props['operation'] operation = props['operation']
lazy_value_func = input_sockets['Expr'][ct.FlowKind.LazyValueFunc] lazy_func = input_sockets['Expr'][ct.FlowKind.Func]
info = input_sockets['Expr'][ct.FlowKind.Info] info = input_sockets['Expr'][ct.FlowKind.Info]
has_lazy_value_func = not ct.FlowSignal.check(lazy_value_func) has_lazy_func = not ct.FlowSignal.check(lazy_func)
has_info = not ct.FlowSignal.check(info) has_info = not ct.FlowSignal.check(info)
# Dimension(s) # Dimension(s)
dim_0 = props['dim_0'] dim_0 = props['dim_0']
dim_1 = props['dim_1'] dim_1 = props['dim_1']
if ( if (
has_lazy_value_func has_lazy_func
and has_info and has_info
and operation is not None and operation is not None
and operation.are_dims_valid(info, dim_0, dim_1) and operation.are_dims_valid(info, dim_0, dim_1)
@ -570,7 +570,7 @@ class FilterMathNode(base.MaxwellSimNode):
else None else None
) )
return lazy_value_func.compose_within( return lazy_func.compose_within(
operation.jax_func(axis_0, axis_1, slice_tuple), operation.jax_func(axis_0, axis_1, slice_tuple),
enclosing_func_args=operation.func_args, enclosing_func_args=operation.func_args,
supports_jax=True, supports_jax=True,
@ -594,14 +594,14 @@ class FilterMathNode(base.MaxwellSimNode):
input_sockets={'Expr', 'Dim'}, input_sockets={'Expr', 'Dim'},
input_socket_kinds={ input_socket_kinds={
'Expr': ct.FlowKind.Info, 'Expr': ct.FlowKind.Info,
'Dim': {ct.FlowKind.LazyValueFunc, ct.FlowKind.Params, ct.FlowKind.Info}, 'Dim': {ct.FlowKind.Func, ct.FlowKind.Params, ct.FlowKind.Info},
}, },
input_sockets_optional={'Dim': True}, input_sockets_optional={'Dim': True},
) )
def compute_info(self, props, input_sockets) -> ct.InfoFlow: def compute_info(self, props, input_sockets) -> ct.InfoFlow:
operation = props['operation'] operation = props['operation']
info = input_sockets['Expr'] info = input_sockets['Expr']
dim_coords = input_sockets['Dim'][ct.FlowKind.LazyValueFunc] dim_coords = input_sockets['Dim'][ct.FlowKind.Func]
dim_params = input_sockets['Dim'][ct.FlowKind.Params] dim_params = input_sockets['Dim'][ct.FlowKind.Params]
dim_info = input_sockets['Dim'][ct.FlowKind.Info] dim_info = input_sockets['Dim'][ct.FlowKind.Info]
dim_symbol = props['set_dim_symbol'] dim_symbol = props['set_dim_symbol']

View File

@ -401,7 +401,7 @@ class MapMathNode(base.MaxwellSimNode):
The name and type of the available symbol is clearly shown, and most valid `sympy` expressions that you would expect to work, should work. The name and type of the available symbol is clearly shown, and most valid `sympy` expressions that you would expect to work, should work.
Use of expressions generally imposes no performance penalty: Just like the baked-in operations, it is compiled to a high-performance `jax` function. Use of expressions generally imposes no performance penalty: Just like the baked-in operations, it is compiled to a high-performance `jax` function.
Thus, it participates in the `ct.FlowKind.LazyValueFunc` composition chain. Thus, it participates in the `ct.FlowKind.Func` composition chain.
Attributes: Attributes:
@ -412,10 +412,10 @@ class MapMathNode(base.MaxwellSimNode):
bl_label = 'Map Math' bl_label = 'Map Math'
input_sockets: typ.ClassVar = { input_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
} }
output_sockets: typ.ClassVar = { output_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
} }
#################### ####################
@ -474,7 +474,7 @@ class MapMathNode(base.MaxwellSimNode):
layout.prop(self, self.blfields['operation'], text='') layout.prop(self, self.blfields['operation'], text='')
#################### ####################
# - FlowKind.Value|LazyValueFunc # - FlowKind.Value|Func
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
@ -497,16 +497,14 @@ class MapMathNode(base.MaxwellSimNode):
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
kind=ct.FlowKind.LazyValueFunc, kind=ct.FlowKind.Func,
props={'operation'}, props={'operation'},
input_sockets={'Expr'}, input_sockets={'Expr'},
input_socket_kinds={ input_socket_kinds={
'Expr': ct.FlowKind.LazyValueFunc, 'Expr': ct.FlowKind.Func,
}, },
) )
def compute_func( def compute_func(self, props, input_sockets) -> ct.FuncFlow | ct.FlowSignal:
self, props, input_sockets
) -> ct.LazyValueFuncFlow | ct.FlowSignal:
operation = props['operation'] operation = props['operation']
expr = input_sockets['Expr'] expr = input_sockets['Expr']

View File

@ -261,12 +261,12 @@ class OperateMathNode(base.MaxwellSimNode):
bl_label = 'Operate Math' bl_label = 'Operate Math'
input_sockets: typ.ClassVar = { input_sockets: typ.ClassVar = {
'Expr L': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr L': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
'Expr R': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr R': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
} }
output_sockets: typ.ClassVar = { output_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef( 'Expr': sockets.ExprSocketDef(
active_kind=ct.FlowKind.LazyValueFunc, show_info_columns=True active_kind=ct.FlowKind.Func, show_info_columns=True
), ),
} }
@ -344,7 +344,7 @@ class OperateMathNode(base.MaxwellSimNode):
layout.prop(self, self.blfields['operation'], text='') layout.prop(self, self.blfields['operation'], text='')
#################### ####################
# - FlowKind.Value|LazyValueFunc # - FlowKind.Value|Func
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
@ -373,12 +373,12 @@ class OperateMathNode(base.MaxwellSimNode):
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
kind=ct.FlowKind.LazyValueFunc, kind=ct.FlowKind.Func,
props={'operation'}, props={'operation'},
input_sockets={'Expr L', 'Expr R'}, input_sockets={'Expr L', 'Expr R'},
input_socket_kinds={ input_socket_kinds={
'Expr L': ct.FlowKind.LazyValueFunc, 'Expr L': ct.FlowKind.Func,
'Expr R': ct.FlowKind.LazyValueFunc, 'Expr R': ct.FlowKind.Func,
}, },
) )
def compose_func(self, props: dict, input_sockets: dict): def compose_func(self, props: dict, input_sockets: dict):

View File

@ -97,7 +97,7 @@ class ReduceMathNode(base.MaxwellSimNode):
'Data', 'Data',
props={'active_socket_set', 'operation'}, props={'active_socket_set', 'operation'},
input_sockets={'Data', 'Axis', 'Reducer'}, input_sockets={'Data', 'Axis', 'Reducer'},
input_socket_kinds={'Reducer': ct.FlowKind.LazyValueFunc}, input_socket_kinds={'Reducer': ct.FlowKind.Func},
input_sockets_optional={'Reducer': True}, input_sockets_optional={'Reducer': True},
) )
def compute_data(self, props: dict, input_sockets: dict): def compute_data(self, props: dict, input_sockets: dict):

View File

@ -258,10 +258,10 @@ class TransformMathNode(base.MaxwellSimNode):
bl_label = 'Transform Math' bl_label = 'Transform Math'
input_sockets: typ.ClassVar = { input_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
} }
output_sockets: typ.ClassVar = { output_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
} }
#################### ####################
@ -323,7 +323,7 @@ class TransformMathNode(base.MaxwellSimNode):
layout.prop(self, self.blfields['operation'], text='') layout.prop(self, self.blfields['operation'], text='')
#################### ####################
# - Compute: LazyValueFunc / Array # - Compute: Func / Array
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
@ -346,16 +346,14 @@ class TransformMathNode(base.MaxwellSimNode):
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
kind=ct.FlowKind.LazyValueFunc, kind=ct.FlowKind.Func,
props={'operation'}, props={'operation'},
input_sockets={'Expr'}, input_sockets={'Expr'},
input_socket_kinds={ input_socket_kinds={
'Expr': ct.FlowKind.LazyValueFunc, 'Expr': ct.FlowKind.Func,
}, },
) )
def compute_func( def compute_func(self, props, input_sockets) -> ct.FuncFlow | ct.FlowSignal:
self, props, input_sockets
) -> ct.LazyValueFuncFlow | ct.FlowSignal:
operation = props['operation'] operation = props['operation']
expr = input_sockets['Expr'] expr = input_sockets['Expr']

View File

@ -209,7 +209,7 @@ class VizNode(base.MaxwellSimNode):
#################### ####################
input_sockets: typ.ClassVar = { input_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef( 'Expr': sockets.ExprSocketDef(
active_kind=ct.FlowKind.LazyValueFunc, active_kind=ct.FlowKind.Func,
default_symbols=[sim_symbols.x], default_symbols=[sim_symbols.x],
default_value=2 * sim_symbols.x.sp_symbol, default_value=2 * sim_symbols.x.sp_symbol,
), ),
@ -370,7 +370,7 @@ class VizNode(base.MaxwellSimNode):
props={'viz_mode', 'viz_target', 'colormap'}, props={'viz_mode', 'viz_target', 'colormap'},
input_sockets={'Expr'}, input_sockets={'Expr'},
input_socket_kinds={ input_socket_kinds={
'Expr': {ct.FlowKind.LazyValueFunc, ct.FlowKind.Info, ct.FlowKind.Params} 'Expr': {ct.FlowKind.Func, ct.FlowKind.Info, ct.FlowKind.Params}
}, },
all_loose_input_sockets=True, all_loose_input_sockets=True,
) )
@ -382,7 +382,7 @@ class VizNode(base.MaxwellSimNode):
props={'viz_mode', 'viz_target', 'colormap'}, props={'viz_mode', 'viz_target', 'colormap'},
input_sockets={'Expr'}, input_sockets={'Expr'},
input_socket_kinds={ input_socket_kinds={
'Expr': {ct.FlowKind.LazyValueFunc, ct.FlowKind.Info, ct.FlowKind.Params} 'Expr': {ct.FlowKind.Func, ct.FlowKind.Info, ct.FlowKind.Params}
}, },
unit_systems={'BlenderUnits': ct.UNITS_BLENDER}, unit_systems={'BlenderUnits': ct.UNITS_BLENDER},
all_loose_input_sockets=True, all_loose_input_sockets=True,
@ -392,7 +392,7 @@ class VizNode(base.MaxwellSimNode):
self, managed_objs, props, input_sockets, loose_input_sockets, unit_systems self, managed_objs, props, input_sockets, loose_input_sockets, unit_systems
): ):
# Retrieve Inputs # Retrieve Inputs
lazy_value_func = input_sockets['Expr'][ct.FlowKind.LazyValueFunc] lazy_func = input_sockets['Expr'][ct.FlowKind.Func]
info = input_sockets['Expr'][ct.FlowKind.Info] info = input_sockets['Expr'][ct.FlowKind.Info]
params = input_sockets['Expr'][ct.FlowKind.Params] params = input_sockets['Expr'][ct.FlowKind.Params]
@ -422,9 +422,9 @@ class VizNode(base.MaxwellSimNode):
for sym in params.sorted_symbols for sym in params.sorted_symbols
} }
# Realize LazyValueFunc w/Symbolic Values, Unit System # Realize Func w/Symbolic Values, Unit System
## -> This gives us the actual plot data! ## -> This gives us the actual plot data!
data = lazy_value_func.func_jax( data = lazy_func.func_jax(
*params.scaled_func_args( *params.scaled_func_args(
unit_systems['BlenderUnits'], symbol_values=symbol_values unit_systems['BlenderUnits'], symbol_values=symbol_values
), ),

View File

@ -29,12 +29,12 @@ class ExprConstantNode(base.MaxwellSimNode):
input_sockets: typ.ClassVar = { input_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef( 'Expr': sockets.ExprSocketDef(
active_kind=ct.FlowKind.LazyValueFunc, active_kind=ct.FlowKind.Func,
), ),
} }
output_sockets: typ.ClassVar = { output_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef( 'Expr': sockets.ExprSocketDef(
active_kind=ct.FlowKind.LazyValueFunc, active_kind=ct.FlowKind.Func,
show_info_columns=True, show_info_columns=True,
), ),
} }
@ -58,12 +58,12 @@ class ExprConstantNode(base.MaxwellSimNode):
@events.computes_output_socket( @events.computes_output_socket(
# Trigger # Trigger
'Expr', 'Expr',
kind=ct.FlowKind.LazyValueFunc, kind=ct.FlowKind.Func,
# Loaded # Loaded
input_sockets={'Expr'}, input_sockets={'Expr'},
input_socket_kinds={'Expr': ct.FlowKind.LazyValueFunc}, input_socket_kinds={'Expr': ct.FlowKind.Func},
) )
def compute_lazy_value_func(self, input_sockets: dict) -> typ.Any: def compute_lazy_func(self, input_sockets: dict) -> typ.Any:
return input_sockets['Expr'] return input_sockets['Expr']
#################### ####################

View File

@ -42,7 +42,7 @@ class DataFileImporterNode(base.MaxwellSimNode):
'File Path': sockets.FilePathSocketDef(), 'File Path': sockets.FilePathSocketDef(),
} }
output_sockets: typ.ClassVar = { output_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
} }
#################### ####################
@ -121,11 +121,11 @@ class DataFileImporterNode(base.MaxwellSimNode):
pass pass
#################### ####################
# - FlowKind.Array|LazyValueFunc # - FlowKind.Array|Func
#################### ####################
@events.computes_output_socket( @events.computes_output_socket(
'Expr', 'Expr',
kind=ct.FlowKind.LazyValueFunc, kind=ct.FlowKind.Func,
input_sockets={'File Path'}, input_sockets={'File Path'},
) )
def compute_func(self, input_sockets: dict) -> td.Simulation: def compute_func(self, input_sockets: dict) -> td.Simulation:
@ -144,7 +144,7 @@ class DataFileImporterNode(base.MaxwellSimNode):
# Jax Compatibility: Lazy Data Loading # Jax Compatibility: Lazy Data Loading
## -> Delay loading of data from file as long as we can. ## -> Delay loading of data from file as long as we can.
if data_file_format.loader_is_jax_compatible: if data_file_format.loader_is_jax_compatible:
return ct.LazyValueFuncFlow( return ct.FuncFlow(
func=lambda: data_file_format.loader(file_path), func=lambda: data_file_format.loader(file_path),
supports_jax=True, supports_jax=True,
) )
@ -152,7 +152,7 @@ class DataFileImporterNode(base.MaxwellSimNode):
# No Jax Compatibility: Eager Data Loading # No Jax Compatibility: Eager Data Loading
## -> Load the data now and bind it. ## -> Load the data now and bind it.
data = data_file_format.loader(file_path) data = data_file_format.loader(file_path)
return ct.LazyValueFuncFlow(func=lambda: data, supports_jax=True) return ct.FuncFlow(func=lambda: data, supports_jax=True)
return ct.FlowSignal.FlowPending return ct.FlowSignal.FlowPending
return ct.FlowSignal.FlowPending return ct.FlowSignal.FlowPending
@ -175,7 +175,7 @@ class DataFileImporterNode(base.MaxwellSimNode):
'Expr', 'Expr',
kind=ct.FlowKind.Info, kind=ct.FlowKind.Info,
output_sockets={'Expr'}, output_sockets={'Expr'},
output_socket_kinds={'Expr': ct.FlowKind.LazyValueFunc}, output_socket_kinds={'Expr': ct.FlowKind.Func},
) )
def compute_info(self, output_sockets) -> ct.InfoFlow: def compute_info(self, output_sockets) -> ct.InfoFlow:
"""Declare an `InfoFlow` based on the data shape. """Declare an `InfoFlow` based on the data shape.

View File

@ -66,7 +66,7 @@ class DataFileExporterNode(base.MaxwellSimNode):
bl_label = 'Data File Importer' bl_label = 'Data File Importer'
input_sockets: typ.ClassVar = { input_sockets: typ.ClassVar = {
'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.LazyValueFunc), 'Expr': sockets.ExprSocketDef(active_kind=ct.FlowKind.Func),
'File Path': sockets.FilePathSocketDef(), 'File Path': sockets.FilePathSocketDef(),
} }
@ -95,8 +95,8 @@ class DataFileExporterNode(base.MaxwellSimNode):
@property @property
def expr_data(self) -> typ.Any | None: def expr_data(self) -> typ.Any | None:
"""Retrieve the input expression's data by evaluating its `LazyValueFunc`.""" """Retrieve the input expression's data by evaluating its `Func`."""
func = self._compute_input('Expr', kind=ct.FlowKind.LazyValueFunc) func = self._compute_input('Expr', kind=ct.FlowKind.Func)
params = self._compute_input('Expr', kind=ct.FlowKind.Params) params = self._compute_input('Expr', kind=ct.FlowKind.Params)
has_func = not ct.FlowSignal.check(func) has_func = not ct.FlowSignal.check(func)

View File

@ -133,7 +133,7 @@ class TemporalShapeNode(base.MaxwellSimNode):
}, },
input_socket_kinds={ input_socket_kinds={
't Range': ct.FlowKind.Range, 't Range': ct.FlowKind.Range,
'Envelope': ct.FlowKind.LazyValueFunc, 'Envelope': ct.FlowKind.Func,
}, },
input_sockets_optional={ input_sockets_optional={
'max E': True, 'max E': True,

View File

@ -525,9 +525,9 @@ class MaxwellSimSocket(bpy.types.NodeSocket, bl_instance.BLInstance):
msg = f'Socket {self.bl_label} {self.socket_type}): Tried to set "ct.FlowKind.Array", but socket does not define it' msg = f'Socket {self.bl_label} {self.socket_type}): Tried to set "ct.FlowKind.Array", but socket does not define it'
raise NotImplementedError(msg) raise NotImplementedError(msg)
# LazyValueFunc # Func
@property @property
def lazy_value_func(self) -> ct.LazyValueFuncFlow: def lazy_func(self) -> ct.FuncFlow:
"""Throws a descriptive error. """Throws a descriptive error.
Notes: Notes:
@ -538,8 +538,8 @@ class MaxwellSimSocket(bpy.types.NodeSocket, bl_instance.BLInstance):
""" """
return ct.FlowSignal.NoFlow return ct.FlowSignal.NoFlow
@lazy_value_func.setter @lazy_func.setter
def lazy_value_func(self, lazy_value_func: ct.LazyValueFuncFlow) -> None: def lazy_func(self, lazy_func: ct.FuncFlow) -> None:
"""Throws a descriptive error. """Throws a descriptive error.
Notes: Notes:
@ -548,7 +548,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket, bl_instance.BLInstance):
Raises: Raises:
NotImplementedError: When used without being overridden. NotImplementedError: When used without being overridden.
""" """
msg = f'Socket {self.bl_label} {self.socket_type}): Tried to set "ct.FlowKind.LazyValueFunc", but socket does not define it' msg = f'Socket {self.bl_label} {self.socket_type}): Tried to set "ct.FlowKind.Func", but socket does not define it'
raise NotImplementedError(msg) raise NotImplementedError(msg)
# Range # Range
@ -595,7 +595,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket, bl_instance.BLInstance):
kind_data_map = { kind_data_map = {
ct.FlowKind.Value: lambda: self.value, ct.FlowKind.Value: lambda: self.value,
ct.FlowKind.Array: lambda: self.array, ct.FlowKind.Array: lambda: self.array,
ct.FlowKind.LazyValueFunc: lambda: self.lazy_value_func, ct.FlowKind.Func: lambda: self.lazy_func,
ct.FlowKind.Range: lambda: self.lazy_range, ct.FlowKind.Range: lambda: self.lazy_range,
ct.FlowKind.Params: lambda: self.params, ct.FlowKind.Params: lambda: self.params,
ct.FlowKind.Info: lambda: self.info, ct.FlowKind.Info: lambda: self.info,
@ -784,7 +784,7 @@ class MaxwellSimSocket(bpy.types.NodeSocket, bl_instance.BLInstance):
ct.FlowKind.Value: self.draw_value, ct.FlowKind.Value: self.draw_value,
ct.FlowKind.Array: self.draw_array, ct.FlowKind.Array: self.draw_array,
ct.FlowKind.Range: self.draw_lazy_range, ct.FlowKind.Range: self.draw_lazy_range,
ct.FlowKind.LazyValueFunc: self.draw_lazy_value_func, ct.FlowKind.Func: self.draw_lazy_func,
}[self.active_kind](col) }[self.active_kind](col)
# Info Drawing # Info Drawing
@ -914,11 +914,11 @@ class MaxwellSimSocket(bpy.types.NodeSocket, bl_instance.BLInstance):
col: Target for defining UI elements. col: Target for defining UI elements.
""" """
def draw_lazy_value_func(self, col: bpy.types.UILayout) -> None: def draw_lazy_func(self, col: bpy.types.UILayout) -> None:
"""Draws the socket lazy value function UI on its own line. """Draws the socket lazy value function UI on its own line.
Notes: Notes:
Should be overriden by individual socket classes, if they have an editable `FlowKind.LazyValueFunc`. Should be overriden by individual socket classes, if they have an editable `FlowKind.Func`.
Parameters: Parameters:
col: Target for defining UI elements. col: Target for defining UI elements.

View File

@ -100,7 +100,7 @@ class ExprBLSocket(base.MaxwellSimSocket):
When active, `self.active_unit` can be used via the UI to select valid unit of the given `self.physical_type`, and `self.unit` works. When active, `self.active_unit` can be used via the UI to select valid unit of the given `self.physical_type`, and `self.unit` works.
The enum itself can be dynamically altered, ex. via its UI dropdown support. The enum itself can be dynamically altered, ex. via its UI dropdown support.
symbols: The symbolic variables valid in the context of the expression. symbols: The symbolic variables valid in the context of the expression.
Various features, including `LazyValueFunc` support, become available when symbols are in use. Various features, including `Func` support, become available when symbols are in use.
The presence of symbols forces fallback to a string-based `sympy` expression UI. The presence of symbols forces fallback to a string-based `sympy` expression UI.
active_unit: The currently active unit, as a dropdown. active_unit: The currently active unit, as a dropdown.
@ -544,20 +544,20 @@ class ExprBLSocket(base.MaxwellSimSocket):
] ]
#################### ####################
# - FlowKind: LazyValueFunc (w/Params if Constant) # - FlowKind: Func (w/Params if Constant)
#################### ####################
@property @property
def lazy_value_func(self) -> ct.LazyValueFuncFlow: def lazy_func(self) -> ct.FuncFlow:
"""Returns a lazy value that computes the expression returned by `self.value`. """Returns a lazy value that computes the expression returned by `self.value`.
If `self.value` has unknown symbols (as indicated by `self.symbols`), then these will be the arguments of the `LazyValueFuncFlow`. If `self.value` has unknown symbols (as indicated by `self.symbols`), then these will be the arguments of the `FuncFlow`.
Otherwise, the returned lazy value function will be a simple excuse for `self.params` to pass the verbatim `self.value`. Otherwise, the returned lazy value function will be a simple excuse for `self.params` to pass the verbatim `self.value`.
""" """
# Symbolic # Symbolic
## -> `self.value` is guaranteed to be an expression with unknowns. ## -> `self.value` is guaranteed to be an expression with unknowns.
## -> The function computes `self.value` with unknowns as arguments. ## -> The function computes `self.value` with unknowns as arguments.
if self.symbols: if self.symbols:
return ct.LazyValueFuncFlow( return ct.FuncFlow(
func=sp.lambdify( func=sp.lambdify(
self.sorted_symbols, self.sorted_symbols,
spux.scale_to_unit(self.value, self.unit), spux.scale_to_unit(self.value, self.unit),
@ -572,7 +572,7 @@ class ExprBLSocket(base.MaxwellSimSocket):
## -> ("Dummy" as in returns the same argument that it takes). ## -> ("Dummy" as in returns the same argument that it takes).
## -> This is an excuse to let `ParamsFlow` pass `self.value` verbatim. ## -> This is an excuse to let `ParamsFlow` pass `self.value` verbatim.
## -> Generally only useful for operations with other expressions. ## -> Generally only useful for operations with other expressions.
return ct.LazyValueFuncFlow( return ct.FuncFlow(
func=lambda v: v, func=lambda v: v,
func_args=[ func_args=[
self.physical_type if self.physical_type is not None else self.mathtype self.physical_type if self.physical_type is not None else self.mathtype
@ -582,7 +582,7 @@ class ExprBLSocket(base.MaxwellSimSocket):
@property @property
def params(self) -> ct.ParamsFlow: def params(self) -> ct.ParamsFlow:
"""Returns parameter symbols/values to accompany `self.lazy_value_func`. """Returns parameter symbols/values to accompany `self.lazy_func`.
If `self.value` has unknown symbols (as indicated by `self.symbols`), then these will be passed into `ParamsFlow`, which will thus be parameterized (and require realization before use). If `self.value` has unknown symbols (as indicated by `self.symbols`), then these will be passed into `ParamsFlow`, which will thus be parameterized (and require realization before use).
Otherwise, `self.value` is passed verbatim as the only `ParamsFlow.func_arg`. Otherwise, `self.value` is passed verbatim as the only `ParamsFlow.func_arg`.
@ -605,13 +605,13 @@ class ExprBLSocket(base.MaxwellSimSocket):
@property @property
def info(self) -> ct.ArrayFlow: def info(self) -> ct.ArrayFlow:
r"""Returns parameter symbols/values to accompany `self.lazy_value_func`. r"""Returns parameter symbols/values to accompany `self.lazy_func`.
The output name/size/mathtype/unit corresponds directly the `ExprSocket`. The output name/size/mathtype/unit corresponds directly the `ExprSocket`.
If `self.symbols` has entries, then these will propagate as dimensions with unresolvable `RangeFlow` index descriptions. If `self.symbols` has entries, then these will propagate as dimensions with unresolvable `RangeFlow` index descriptions.
The index range will be $(-\infty,\infty)$, with $0$ steps and no unit. The index range will be $(-\infty,\infty)$, with $0$ steps and no unit.
The order/naming matches `self.params` and `self.lazy_value_func`. The order/naming matches `self.params` and `self.lazy_func`.
Otherwise, only the output name/size/mathtype/unit corresponding to the socket is passed along. Otherwise, only the output name/size/mathtype/unit corresponding to the socket is passed along.
""" """
@ -835,13 +835,13 @@ class ExprBLSocket(base.MaxwellSimSocket):
if self.steps != 0: if self.steps != 0:
col.prop(self, self.blfields['steps'], text='') col.prop(self, self.blfields['steps'], text='')
def draw_lazy_value_func(self, col: bpy.types.UILayout) -> None: def draw_lazy_func(self, col: bpy.types.UILayout) -> None:
"""Draw the socket body for a single flexible value/expression, for down-chain lazy evaluation. """Draw the socket body for a single flexible value/expression, for down-chain lazy evaluation.
This implements the most flexible variant of the `ExprSocket` UI, providing the user with full runtime-configuration of the exact `self.size`, `self.mathtype`, `self.physical_type`, and `self.symbols` of the expression. This implements the most flexible variant of the `ExprSocket` UI, providing the user with full runtime-configuration of the exact `self.size`, `self.mathtype`, `self.physical_type`, and `self.symbols` of the expression.
Notes: Notes:
Drawn when `self.active_kind == FlowKind.LazyValueFunc`. Drawn when `self.active_kind == FlowKind.Func`.
This is an ideal choice for ex. math nodes that need to accept arbitrary expressions as inputs, with an eye towards lazy evaluation of ex. symbolic terms. This is an ideal choice for ex. math nodes that need to accept arbitrary expressions as inputs, with an eye towards lazy evaluation of ex. symbolic terms.
@ -872,7 +872,7 @@ class ExprBLSocket(base.MaxwellSimSocket):
# - UI: InfoFlow # - UI: InfoFlow
#################### ####################
def draw_info(self, info: ct.InfoFlow, col: bpy.types.UILayout) -> None: def draw_info(self, info: ct.InfoFlow, col: bpy.types.UILayout) -> None:
if self.active_kind == ct.FlowKind.LazyValueFunc and self.show_info_columns: if self.active_kind == ct.FlowKind.Func and self.show_info_columns:
row = col.row() row = col.row()
box = row.box() box = row.box()
grid = box.grid_flow( grid = box.grid_flow(
@ -927,7 +927,7 @@ class ExprSocketDef(base.SocketDef):
ct.FlowKind.Value, ct.FlowKind.Value,
ct.FlowKind.Range, ct.FlowKind.Range,
ct.FlowKind.Array, ct.FlowKind.Array,
ct.FlowKind.LazyValueFunc, ct.FlowKind.Func,
] = ct.FlowKind.Value ] = ct.FlowKind.Value
# Socket Interface # Socket Interface