Non-monotone feature embedding#
The problem#
A MonoLinear layer constrains its weights to |W| (non-negative effective
weights), so the network is monotone non-decreasing in every input it sees.
With a convex activation (e.g. ReLU) and non-negative weights it can represent
only convex monotone functions (Sartor 2025 Prop 3.2). But real tabular
datasets mix monotone-constrained features (where domain knowledge fixes a
direction) with non-monotone (“free”) features that must be allowed an
arbitrary, unconstrained effect on the output. Feeding a free feature straight
into the monotone stack would wrongly force the output to be monotone in it.
The construction#
Free features are routed through an unconstrained Dense embedding; its
output is concatenated with the monotone-feature channels and the concatenation
is fed to the monotone stack. The network stays monotone in the declared
monotone inputs, while the free features reach the output only through the
unconstrained embedding — so their net effect can be arbitrary. This is the
standard embedding-composition trick the builder already implements.
Why the embedding needs two Dense layers#
For the free branch to represent an arbitrary function of the free features it
must be a universal approximator, which needs a hidden layer plus an output
projection — two weight layers (Linear → act → Linear). It is tempting to drop
the embedding’s output layer and let the monotone stack’s first MonoLinear
absorb it (two adjacent linear maps merge). That merge is invalid here: the
first MonoLinear is |W|-constrained, so it can only form non-negative
combinations of the embedding’s hidden units, and by Prop 3.2 that is restricted
to convex functions of them — not a free output projection. A single Dense
layer therefore collapses the free branch’s expressivity to convex, which is not
universal.
Giving the embedding its own unconstrained output layer (the second Dense)
computes the arbitrary free-feature function before the constrained stack,
which then reads it with a positive passthrough weight. Hence two Dense
layers (at the model width) for the non-monotone branch. (Fully-monotone
datasets have no free features, so the branch is empty and this is a no-op.)