Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .github/skills/cellar/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
name: cellar
description: Look up the public API of any JVM dependency (Scala 3, Scala 2, Java) from the terminal. Returns type signatures, members, docstrings, and source code as Markdown.
---

# Cellar

Use cellar to look up the API of JVM dependencies instead of guessing or hallucinating signatures. You find the executable in `scripts/cellar`. e.g.

```sh
# Look up a Scala 3 trait
scripts/cellar get-external org.typelevel:cats-core_3:2.10.0 cats.Monad
```
Translate the examples below to this pattern, calls `scripts/cellar` instead of `cellar`.

## Project-aware commands (run from project root)

Query the current project's code and all its dependencies. Cellar auto-detects the build tool (Mill, sbt, scala-cli).

scripts/cellar get [--module <name>] <fqn> # single symbol (signature, members, docs)
scripts/cellar list [--module <name>] <package> # list symbols in a package or class
scripts/cellar search [--module <name>] <query> # case-insensitive substring search

- Mill/sbt projects: `--module` is required (e.g. `--module lib`, `--module core`)
- scala-cli projects: omit `--module`
- `--no-cache`: skip classpath cache, re-extract from build tool
- `--java-home <path>`: override JRE classpath
- `-l`, `--limit <N>`: max results for `list`/`search` (default: 50)

## External commands (query arbitrary Maven coordinates)

Query any published artifact by explicit coordinate (`group:artifact:version`):

cellar get-external <coordinate> <fqn> # single symbol
cellar list-external <coordinate> <package> # list symbols
cellar search-external <coordinate> <query> # search by name
cellar get-source <coordinate> <fqn> # fetch source code
cellar deps <coordinate> # dependency tree

- Coordinates must be explicit: `group:artifact_3:version` (no `::` shorthand)
- For sbt plugins, use the full Scala and sbt suffix: `group:artifact_2.12_1.0:version` (e.g. `org.scala-native:sbt-scala-native_2.12_1.0:latest`)
- For compiler plugins and other artifacts with full Scala version suffixes, use the full version: `group:artifact_3.3.8:version`
- Use `latest` as the version to resolve the most recent release
- `-r`, `--repository <url>`: extra Maven repository (repeatable)

## Workflow

1. **Don't know the package?** → `cellar search` / `cellar search-external`
2. **Know the package, not the type?** → `cellar list` / `cellar list-external`
3. **Know the type?** → `cellar get` / `cellar get-external`
4. **Need the implementation?** → `cellar get-source`

## Examples

```sh
# Look up a Scala 3 trait
cellar get-external org.typelevel:cats-core_3:2.10.0 cats.Monad

# Look up a Java class
cellar get-external org.apache.commons:commons-lang3:3.14.0 org.apache.commons.lang3.StringUtils

# List a package
cellar list-external io.circe:circe-core_3:0.14.6 io.circe

# Search for a method
cellar search-external org.typelevel:cats-core_3:2.10.0 flatMap

# Get source code
cellar get-source org.typelevel:cats-core_3:2.10.0 cats.Monad

# Dependency tree
cellar deps org.typelevel:cats-effect_3:3.5.4

# sbt plugin (use full Scala + sbt suffix)
cellar deps org.scala-native:sbt-scala-native_2.12_1.0:latest

# Project-aware (from a Mill project root)
cellar get --module lib cats.Monad
cellar list --module core cats
cellar search --module lib flatMap
```

## Output

- **stdout**: Markdown — ready to consume directly
- **stderr**: diagnostics (warnings, truncation notices)
- **Exit 0**: success, **Exit 1**: error
File renamed without changes.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,9 @@ biases1.csv
biases2.csv
.DS_Store

scripts/cellar

.venv/
experiments/src/bhd.scala

.cellar/
32 changes: 25 additions & 7 deletions vecxt/src/ndarrayFloatOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object NDArrayFloatOps:
mkNDArray(out, a.shape.clone(), colMajorStrides(a.shape), 0)
end binaryOpGeneral

private[NDArrayFloatOps] def unaryOpGeneral(a: NDArray[Float], f: Float => Float): NDArray[Float] =
private[NDArrayFloatOps] inline def unaryOpGeneral(a: NDArray[Float], inline f: Float => Float): NDArray[Float] =
val n = a.numel
val ndim = a.ndim
val out = new Array[Float](n)
Expand Down Expand Up @@ -339,36 +339,54 @@ object NDArrayFloatOps:
@targetName("ndFloatAbs")
inline def abs: NDArray[Float] =
if a.isContiguous then mkNDArray(vecxt.floatarrays.abs(a.data), a.shape, a.strides, 0)
else unaryOpGeneral(a, x => Math.abs(x.toDouble).toFloat)
else unaryOpGeneral(a, x => Math.abs(x))

/** Element-wise natural exponential. */
@targetName("ndFloatExp")
inline def exp: NDArray[Float] =
if a.isContiguous then mkNDArray(vecxt.floatarrays.exp(a.data), a.shape, a.strides, 0)
else unaryOpGeneral(a, x => Math.exp(x.toDouble).toFloat)
else unaryOpGeneral(a, x => Math.exp(x).toFloat)

/** Element-wise natural logarithm. */
@targetName("ndFloatLog")
inline def log: NDArray[Float] =
if a.isContiguous then mkNDArray(vecxt.floatarrays.log(a.data), a.shape, a.strides, 0)
else unaryOpGeneral(a, x => Math.log(x.toDouble).toFloat)
else unaryOpGeneral(a, x => Math.log(x).toFloat)

/** Element-wise square root. */
@targetName("ndFloatSqrt")
inline def sqrt: NDArray[Float] =
if a.isContiguous then mkNDArray(vecxt.floatarrays.sqrt(a.data), a.shape, a.strides, 0)
else unaryOpGeneral(a, x => Math.sqrt(x.toDouble).toFloat)
else unaryOpGeneral(a, x => Math.sqrt(x).toFloat)

/** Element-wise hyperbolic tangent. */
@targetName("ndFloatTanh")
inline def tanh: NDArray[Float] =
if a.isContiguous then mkNDArray(vecxt.floatarrays.tanh(a.data), a.shape, a.strides, 0)
else unaryOpGeneral(a, x => Math.tanh(x.toDouble).toFloat)
else unaryOpGeneral(a, x => Math.tanh(x).toFloat)

/** Element-wise sine. */
@targetName("ndFloatSin")
inline def sin: NDArray[Float] =
if a.isContiguous then mkNDArray(vecxt.floatarrays.sin(a.data), a.shape.clone(), a.strides.clone(), 0)
else unaryOpGeneral(a, x => Math.sin(x).toFloat)

/** Element-wise cosine. */
@targetName("ndFloatCos")
inline def cos: NDArray[Float] =
if a.isContiguous then mkNDArray(vecxt.floatarrays.cos(a.data), a.shape.clone(), a.strides.clone(), 0)
else unaryOpGeneral(a, x => Math.cos(x).toFloat)

/** Element-wise arc tangent. */
@targetName("ndFloatAtan")
inline def atan: NDArray[Float] =
if a.isContiguous then mkNDArray(vecxt.floatarrays.atan(a.data), a.shape.clone(), a.strides.clone(), 0)
else unaryOpGeneral(a, x => Math.atan(x).toFloat)

/** Element-wise sigmoid: `1 / (1 + exp(-x))`. */
@targetName("ndFloatSigmoid")
inline def sigmoid: NDArray[Float] =
val sig = (x: Float) => (1.0 / (1.0 + Math.exp(-x.toDouble))).toFloat
val sig = (x: Float) => (1.0 / (1.0 + Math.exp(-x))).toFloat
if a.isContiguous then mkNDArray(flatUnaryOp(a.data, sig), a.shape, a.strides, 0)
else unaryOpGeneral(a, sig)
end if
Expand Down
41 changes: 41 additions & 0 deletions vecxt/test/src/ndarrayFloatOps.test.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,47 @@ class NDArrayFloatOpsSuite extends FunSuite:
assertEquals(result.toArray.toSeq, Seq(2.0f, 3.0f, 4.0f))
}

test("NDArray[Float] tanh") {
val a = NDArray(Array(0.0f, 1.0f, -1.0f), Array(3))
val result = a.tanh
assertEqualsDouble(result.toArray(0).toDouble, 0.0, 1e-6)
assertEqualsDouble(result.toArray(1).toDouble, Math.tanh(1.0), 1e-6)
assertEqualsDouble(result.toArray(2).toDouble, Math.tanh(-1.0), 1e-6)
}

test("NDArray[Float] sin") {
val a = NDArray(Array(0.0f, (Math.PI / 2).toFloat, Math.PI.toFloat), Array(3))
val result = a.sin
assertEqualsDouble(result.toArray(0).toDouble, 0.0, 1e-6)
assertEqualsDouble(result.toArray(1).toDouble, 1.0, 1e-6)
assertEqualsDouble(result.toArray(2).toDouble, 0.0, 1e-5)
}

test("NDArray[Float] cos") {
val a = NDArray(Array(0.0f, (Math.PI / 2).toFloat, Math.PI.toFloat), Array(3))
val result = a.cos
assertEqualsDouble(result.toArray(0).toDouble, 1.0, 1e-6)
assertEqualsDouble(result.toArray(1).toDouble, 0.0, 1e-5)
assertEqualsDouble(result.toArray(2).toDouble, -1.0, 1e-6)
}

test("NDArray[Float] atan") {
val a = NDArray(Array(0.0f, 1.0f, -1.0f), Array(3))
val result = a.atan
assertEqualsDouble(result.toArray(0).toDouble, 0.0, 1e-6)
assertEqualsDouble(result.toArray(1).toDouble, Math.PI / 4, 1e-6)
assertEqualsDouble(result.toArray(2).toDouble, -Math.PI / 4, 1e-6)
}

test("NDArray[Float] sin on transposed (general kernel)") {
val a = NDArray(Array(0.0f, (Math.PI / 2).toFloat, Math.PI.toFloat, 0.0f), Array(2, 2)).T
val result = a.sin
val expected = a.toArray.map(x => Math.sin(x.toDouble).toFloat)
result.toArray.zip(expected).foreach { (got, exp) =>
assertEqualsDouble(got.toDouble, exp.toDouble, 1e-5)
}
}

test("NDArray[Float] sigmoid at 0 = 0.5") {
val a = NDArray(Array(0.0f), Array(1))
assertEqualsDouble(a.sigmoid.toArray(0).toDouble, 0.5, 1e-6)
Expand Down
Loading