designspace.value#

designspace.value(fn: Callable[[...], Any], *operands: Expr, returns: type) Value#

Compute a derived quantity with your own function.

An escape hatch for a constraint the expression language cannot say: a physical formula, a lookup, a simulation, without inventing a sham custom type just to hang a .prop() on.

The trade is transparency. Prefer a plain expression when you can write one: the library can compute margins from it, narrow domains with .remaining_domain(), and tighten bounds during sampling, none of which it can do through an opaque function. A returns=float value still yields a usable margin; a returns=bool one is fully opaque and has no margin at all.

fn is called with the operands’ values, in order, never the configuration, so everything it reads must be passed as an operand.

Parameters:
  • fn (Callable[..., Any]) – Called with one value per operand. Not serializable: a space containing one cannot be written to JSON or fingerprinted without on_unserializable=”mark”.

  • *operands (Expr) – The expressions whose values fn receives. Every one must be an expression, not a bare literal.

  • returns (type) – What fn returns: int, float, bool, or str.

Returns:

An expression usable as a number or a condition, according to returns.

Return type:

Value

Raises:
  • TypeError – If fn is not callable.

  • ResolutionError – If returns is not a scalar type, or an operand is not an expression.

Examples

>>> def area(w, h):
...     return w * h
>>> s = ds.space(ds.param("w").real(1, 10), ds.param("h").real(1, 10))
>>> s = s.require(ds.value(area, ds.param("w"), ds.param("h"), returns=float) <= 20.0)
>>> s.is_feasible({"w": 2.0, "h": 3.0})
True
>>> s.is_feasible({"w": 9.0, "h": 9.0})
False
>>> s.evaluate_constraints({"w": 2.0, "h": 3.0})[0].margin
14.0