Comparison
7 functions — 7 documented.
equal¶
Element-wise input == other.
Shapes are broadcast by NumPy rules. Broadcasting is implemented by giving the expanded axes stride 0 rather than by materialising a copy — Shape holds strides in bytes and permits 0, so a broadcast operand re-reads the same element instead of allocating an expanded one.
Arithmetic and comparison share a single shader. The operation is a specialisation constant, so the driver folds the switch away at pipeline creation and each variant is as tight as a dedicated shader. Comparisons live there too because everything except the final store is identical — same broadcast indexing, same operand layout, same bounds check — and only the destination element type differs, which is also decided at pipeline creation.
Parameters
- input (Tensor)
- The left operand.
- other (Tensor)
- The right operand, broadcastable against
input.
Returns
A bool tensor of the broadcast shape.
Every comparison against NaN is false, including NaN == NaN, which is IEEE-754 and matches torch. The input dtype and the output dtype are tracked separately — a comparison's output is always bool, and the shader reads the input dtype to decide how to load, a distinction the CPU kernel originally got wrong by checking the output dtype and reading f32 regardless.
Example
>>> vkml.equal(vkml.tensor(np.array([1.0, 2.0], dtype=np.float32)),
... vkml.tensor(np.array([1.0, 3.0], dtype=np.float32))).numpy()
array([ True, False])
Implementation
| Declared in | include/vkml/api/ops.h:29 |
|---|---|
| Graph node | OpKind::Equal |
| CPU kernel | src/backend/cpu/kernels_elementwise.cpp:292 |
| Vulkan shader | composed, or dispatched through a shared kernel |
| Gradient rule | none — backward through it raises |
| History | commits touching the CPU kernel |
| Tests (≥1) | test_vulkan_kernels.py |
See also not_equal, less, where
not_equal¶
Element-wise input != other.
Shapes are broadcast by NumPy rules. Broadcasting is implemented by giving the expanded axes stride 0 rather than by materialising a copy — Shape holds strides in bytes and permits 0, so a broadcast operand re-reads the same element instead of allocating an expanded one.
Arithmetic and comparison share a single shader. The operation is a specialisation constant, so the driver folds the switch away at pipeline creation and each variant is as tight as a dedicated shader. Comparisons live there too because everything except the final store is identical — same broadcast indexing, same operand layout, same bounds check — and only the destination element type differs, which is also decided at pipeline creation.
Parameters
- input (Tensor)
- The left operand.
- other (Tensor)
- The right operand, broadcastable against
input.
Returns
A bool tensor of the broadcast shape.
NaN != NaN is true — the only comparison a NaN satisfies, since every other comparison against NaN is false.
Example
>>> vkml.not_equal(vkml.tensor(np.array([1.0, float('nan')], dtype=np.float32)),
... vkml.tensor(np.array([1.0, float('nan')], dtype=np.float32))).numpy()
array([False, True])
Implementation
| Declared in | include/vkml/api/ops.h:34 |
|---|---|
| Graph node | OpKind::NotEqual |
| CPU kernel | src/backend/cpu/kernels_elementwise.cpp:312 |
| Vulkan shader | composed, or dispatched through a shared kernel |
| Gradient rule | none — backward through it raises |
| History | commits touching the CPU kernel |
| Tests | none found by name |
less¶
Element-wise input < other.
Shapes are broadcast by NumPy rules. Broadcasting is implemented by giving the expanded axes stride 0 rather than by materialising a copy — Shape holds strides in bytes and permits 0, so a broadcast operand re-reads the same element instead of allocating an expanded one.
Arithmetic and comparison share a single shader. The operation is a specialisation constant, so the driver folds the switch away at pipeline creation and each variant is as tight as a dedicated shader. Comparisons live there too because everything except the final store is identical — same broadcast indexing, same operand layout, same bounds check — and only the destination element type differs, which is also decided at pipeline creation.
Parameters
- input (Tensor)
- The left operand.
- other (Tensor)
- The right operand, broadcastable against
input.
Returns
A bool tensor of the broadcast shape.
Every comparison against NaN is false, including NaN == NaN, which is IEEE-754 and matches torch. The input dtype and the output dtype are tracked separately — a comparison's output is always bool, and the shader reads the input dtype to decide how to load, a distinction the CPU kernel originally got wrong by checking the output dtype and reading f32 regardless.
Example
>>> vkml.less(vkml.tensor(np.array([1.0, 3.0], dtype=np.float32)),
... vkml.tensor(np.array([2.0, 2.0], dtype=np.float32))).numpy()
array([ True, False])
Implementation
| Declared in | include/vkml/api/ops.h:30 |
|---|---|
| Graph node | OpKind::Less |
| CPU kernel | src/backend/cpu/kernels_elementwise.cpp:296 |
| Vulkan shader | composed, or dispatched through a shared kernel |
| Gradient rule | none — backward through it raises |
| History | commits touching the CPU kernel |
| Tests (≥1) | test_vulkan_kernels.py |
See also less_equal, greater, where
less_equal¶
Element-wise input <= other.
Shapes are broadcast by NumPy rules. Broadcasting is implemented by giving the expanded axes stride 0 rather than by materialising a copy — Shape holds strides in bytes and permits 0, so a broadcast operand re-reads the same element instead of allocating an expanded one.
Arithmetic and comparison share a single shader. The operation is a specialisation constant, so the driver folds the switch away at pipeline creation and each variant is as tight as a dedicated shader. Comparisons live there too because everything except the final store is identical — same broadcast indexing, same operand layout, same bounds check — and only the destination element type differs, which is also decided at pipeline creation.
Parameters
- input (Tensor)
- The left operand.
- other (Tensor)
- The right operand, broadcastable against
input.
Returns
A bool tensor of the broadcast shape.
Every comparison against NaN is false, including NaN == NaN, which is IEEE-754 and matches torch. The input dtype and the output dtype are tracked separately — a comparison's output is always bool, and the shader reads the input dtype to decide how to load, a distinction the CPU kernel originally got wrong by checking the output dtype and reading f32 regardless.
Example
>>> vkml.less_equal(vkml.tensor(np.array([2.0, 3.0], dtype=np.float32)),
... vkml.tensor(np.array([2.0, 2.0], dtype=np.float32))).numpy()
array([ True, False])
Implementation
| Declared in | include/vkml/api/ops.h:32 |
|---|---|
| Graph node | OpKind::LessEqual |
| CPU kernel | src/backend/cpu/kernels_elementwise.cpp:304 |
| Vulkan shader | composed, or dispatched through a shared kernel |
| Gradient rule | none — backward through it raises |
| History | commits touching the CPU kernel |
| Tests | none found by name |
See also less, greater_equal, relu
greater¶
Element-wise input > other.
Shapes are broadcast by NumPy rules. Broadcasting is implemented by giving the expanded axes stride 0 rather than by materialising a copy — Shape holds strides in bytes and permits 0, so a broadcast operand re-reads the same element instead of allocating an expanded one.
Arithmetic and comparison share a single shader. The operation is a specialisation constant, so the driver folds the switch away at pipeline creation and each variant is as tight as a dedicated shader. Comparisons live there too because everything except the final store is identical — same broadcast indexing, same operand layout, same bounds check — and only the destination element type differs, which is also decided at pipeline creation.
Parameters
- input (Tensor)
- The left operand.
- other (Tensor)
- The right operand, broadcastable against
input.
Returns
A bool tensor of the broadcast shape.
Every comparison against NaN is false, including NaN == NaN, which is IEEE-754 and matches torch. The input dtype and the output dtype are tracked separately — a comparison's output is always bool, and the shader reads the input dtype to decide how to load, a distinction the CPU kernel originally got wrong by checking the output dtype and reading f32 regardless.
Example
>>> vkml.greater(vkml.tensor(np.array([3.0, 1.0], dtype=np.float32)),
... vkml.tensor(np.array([2.0, 2.0], dtype=np.float32))).numpy()
array([ True, False])
Implementation
| Declared in | include/vkml/api/ops.h:31 |
|---|---|
| Graph node | OpKind::Greater |
| CPU kernel | src/backend/cpu/kernels_elementwise.cpp:300 |
| Vulkan shader | composed, or dispatched through a shared kernel |
| Gradient rule | none — backward through it raises |
| History | commits touching the CPU kernel |
| Tests (≥2) | test_ops_vs_torch.py test_vulkan_kernels.py |
See also greater_equal, less, where
greater_equal¶
Element-wise input >= other.
Shapes are broadcast by NumPy rules. Broadcasting is implemented by giving the expanded axes stride 0 rather than by materialising a copy — Shape holds strides in bytes and permits 0, so a broadcast operand re-reads the same element instead of allocating an expanded one.
Arithmetic and comparison share a single shader. The operation is a specialisation constant, so the driver folds the switch away at pipeline creation and each variant is as tight as a dedicated shader. Comparisons live there too because everything except the final store is identical — same broadcast indexing, same operand layout, same bounds check — and only the destination element type differs, which is also decided at pipeline creation.
Parameters
- input (Tensor)
- The left operand.
- other (Tensor)
- The right operand, broadcastable against
input.
Returns
A bool tensor of the broadcast shape.
Every comparison against NaN is false, including NaN == NaN, which is IEEE-754 and matches torch. The input dtype and the output dtype are tracked separately — a comparison's output is always bool, and the shader reads the input dtype to decide how to load, a distinction the CPU kernel originally got wrong by checking the output dtype and reading f32 regardless.
Example
>>> vkml.greater_equal(vkml.tensor(np.array([2.0, 1.0], dtype=np.float32)),
... vkml.tensor(np.array([2.0, 2.0], dtype=np.float32))).numpy()
array([ True, False])
Implementation
| Declared in | include/vkml/api/ops.h:33 |
|---|---|
| Graph node | OpKind::GreaterEqual |
| CPU kernel | src/backend/cpu/kernels_elementwise.cpp:308 |
| Vulkan shader | composed, or dispatched through a shared kernel |
| Gradient rule | none — backward through it raises |
| History | commits touching the CPU kernel |
| Tests | none found by name |
See also greater, less_equal
where¶
Select element-wise from two tensors according to a condition.
condition ? a : b, broadcast across all three operands.
Both branches are always evaluated. That is what element-wise selection means: where chooses between two values that already exist, it does not avoid computing one. Where a branch would produce inf or NaN, the arithmetic still happens and the result is discarded — kl_div relies on exactly this, computing log(0) = -inf and throwing the poisoned product away.
Shapes are broadcast by NumPy rules. Broadcasting is implemented by giving the expanded axes stride 0 rather than by materialising a copy — Shape holds strides in bytes and permits 0, so a broadcast operand re-reads the same element instead of allocating an expanded one.
Parameters
- condition (Tensor)
- A bool tensor.
- input (Tensor)
- Taken where
conditionis true. - other (Tensor)
- Taken where
conditionis false.
Returns
A tensor of the broadcast shape, with input's dtype.
Its push-constant block was repacked to fit the 128-byte guarantee by storing shared extents once instead of per operand — the three operands of a where usually agree on shape, which is what made that possible.
Example
>>> c = vkml.greater(vkml.tensor(np.array([1.0, 3.0], dtype=np.float32)),
... vkml.tensor(np.array([2.0, 2.0], dtype=np.float32)))
>>> vkml.where(c, vkml.tensor(np.array([10.0, 20.0], dtype=np.float32)),
... vkml.tensor(np.array([-1.0, -2.0], dtype=np.float32))).numpy()
array([-1., 20.], dtype=float32)
From the header
Elementwise select. cond must be Bool; all three broadcast together.
Implementation
| Declared in | include/vkml/api/ops.h:87 |
|---|---|
| Graph node | OpKind::Where |
| CPU kernel | src/backend/cpu/kernels_elementwise.cpp:370 |
| Vulkan shader | shaders/where.comp (64 lines · 2 specialisation constants) |
| Gradient rule | autograd.cpp:263 |
| Decisions | ADR 0009 ADR 0013 |
| History | commits touching the CPU kernel |
| Tests (≥8) | test_f16.py test_operand_packing.py test_ops_vs_torch.py test_vulkan_kernels.py |
See also greater, masked_fill, clamp, kl_div