jjinx 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. jjinx-0.0.1/LICENSE +21 -0
  2. jjinx-0.0.1/PKG-INFO +148 -0
  3. jjinx-0.0.1/README.md +106 -0
  4. jjinx-0.0.1/pyproject.toml +49 -0
  5. jjinx-0.0.1/setup.cfg +4 -0
  6. jjinx-0.0.1/src/jinx/__init__.py +0 -0
  7. jjinx-0.0.1/src/jinx/errors.py +41 -0
  8. jjinx-0.0.1/src/jinx/execution/executor.py +60 -0
  9. jjinx-0.0.1/src/jinx/execution/jax/__init__.py +45 -0
  10. jjinx-0.0.1/src/jinx/execution/jax/adverbs.py +91 -0
  11. jjinx-0.0.1/src/jinx/execution/jax/application.py +231 -0
  12. jjinx-0.0.1/src/jinx/execution/jax/verbs.py +90 -0
  13. jjinx-0.0.1/src/jinx/execution/numpy/__init__.py +29 -0
  14. jjinx-0.0.1/src/jinx/execution/numpy/adverbs.py +334 -0
  15. jjinx-0.0.1/src/jinx/execution/numpy/application.py +343 -0
  16. jjinx-0.0.1/src/jinx/execution/numpy/conjunctions.py +437 -0
  17. jjinx-0.0.1/src/jinx/execution/numpy/conversion.py +62 -0
  18. jjinx-0.0.1/src/jinx/execution/numpy/helpers.py +158 -0
  19. jjinx-0.0.1/src/jinx/execution/numpy/printing.py +179 -0
  20. jjinx-0.0.1/src/jinx/execution/numpy/verbs.py +850 -0
  21. jjinx-0.0.1/src/jinx/primitives.py +490 -0
  22. jjinx-0.0.1/src/jinx/shell.py +68 -0
  23. jjinx-0.0.1/src/jinx/vocabulary.py +181 -0
  24. jjinx-0.0.1/src/jinx/word_evaluation.py +375 -0
  25. jjinx-0.0.1/src/jinx/word_formation.py +229 -0
  26. jjinx-0.0.1/src/jinx/word_spelling.py +118 -0
  27. jjinx-0.0.1/src/jjinx.egg-info/PKG-INFO +148 -0
  28. jjinx-0.0.1/src/jjinx.egg-info/SOURCES.txt +33 -0
  29. jjinx-0.0.1/src/jjinx.egg-info/dependency_links.txt +1 -0
  30. jjinx-0.0.1/src/jjinx.egg-info/entry_points.txt +2 -0
  31. jjinx-0.0.1/src/jjinx.egg-info/requires.txt +1 -0
  32. jjinx-0.0.1/src/jjinx.egg-info/top_level.txt +1 -0
  33. jjinx-0.0.1/tests/test_word_evaluation.py +993 -0
  34. jjinx-0.0.1/tests/test_word_formation.py +142 -0
  35. jjinx-0.0.1/tests/test_word_spelling.py +54 -0
jjinx-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Alex Riley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
jjinx-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: jjinx
3
+ Version: 0.0.1
4
+ Summary: Interpreter for the J programming language.
5
+ Author: Alex Riley
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Alex Riley
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/ajcr/jinx
29
+ Keywords: J,interpreter
30
+ Classifier: Development Status :: 3 - Alpha
31
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
32
+ Classifier: Topic :: Utilities
33
+ Classifier: Typing :: Typed
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3.13
37
+ Requires-Python: >=3.13
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: numpy>=2.3.3
41
+ Dynamic: license-file
42
+
43
+ # Jinx
44
+
45
+ ![ci](https://github.com/ajcr/jinx/actions/workflows/ci.yaml/badge.svg?branch=main)
46
+
47
+ An experimental interpreter for the J programming language, built on top of [NumPy](https://numpy.org/).
48
+
49
+ Implements many of J's primitives and tacit programming capabilities, and can be extended to support execution via other frameworks too.
50
+
51
+ ## Executing J
52
+
53
+ Start the interactive shell:
54
+ ```sh
55
+ jinx
56
+ ```
57
+ The shell prompt is four spaces, so commands appear indented. Internally, all multidimensional arrays are NumPy arrays. Verbs, conjunctions and adverbs are a mixture of Python and NumPy methods.
58
+
59
+ Here are some examples what Jinx can do so far:
60
+
61
+ - Solve the "trapping rainwater" problem (solution taken from [here](https://mmapped.blog/posts/04-square-joy-trapped-rain-water)):
62
+ ```j
63
+ +/@((>./\ <. >./\.)-]) 0 1 0 2 1 0 1 3 2 1 2 1
64
+ 6
65
+ ```
66
+ - Compute the correlation between two arrays of numbers (taken from [here](https://stackoverflow.com/a/44845495/3923281)). This is a complex combination of different verbs, adverbs, conjunctions and trains:
67
+ ```j
68
+ 2 1 1 7 9 (+/@:* % *&(+/)&.:*:)&(- +/%#) 6 3 1 5 7
69
+ 0.721332
70
+ ```
71
+ - Create identity matrices in inventive ways (see [this essay](https://code.jsoftware.com/wiki/Essays/Identity_Matrix)):
72
+ ```j
73
+ |.@~:\ @ ($&0) 3
74
+ 1 0 0
75
+ 0 1 0
76
+ 0 0 1
77
+
78
+ (i.@,~ = >: * i.) 3
79
+ 1 0 0
80
+ 0 1 0
81
+ 0 0 1
82
+
83
+ ((={:)\ @ i.) 3
84
+ 1 0 0
85
+ 0 1 0
86
+ 0 0 1
87
+ ```
88
+ - Solve the Josephus problem (see [this essay](https://code.jsoftware.com/wiki/Essays/Josephus_Problem)). Calculate the survivor's number for a circle of people of size N. Note the use of verb obverse and the rank conjunction:
89
+ ```j
90
+ (1&|.&.#:)"0 >: i. 5 10 NB. N ranges from 1 to 50 here (arranged as a table)
91
+ 1 1 3 1 3 5 7 1 3 5
92
+ 7 9 11 13 15 1 3 5 7 9
93
+ 11 13 15 17 19 21 23 25 27 29
94
+ 31 1 3 5 7 9 11 13 15 17
95
+ 19 21 23 25 27 29 31 33 35 37
96
+ ```
97
+ - Build nested boxes containing heterogeneous data types and print the contents:
98
+ ```j
99
+ (<<'abc'),(<(<'de',.'fg'),(<<i. 5 2)),(<(<"0 ] % i. 2 2 3))
100
+ ┌─────┬──────────┬────────────────────────────┐
101
+ │┌───┐│┌──┬─────┐│┌────────┬────────┬────────┐│
102
+ ││abc│││df│┌───┐│││_ │1 │0.5 ││
103
+ │└───┘││eg││0 1│││├────────┼────────┼────────┤│
104
+ │ ││ ││2 3││││0.333333│0.25 │0.2 ││
105
+ │ ││ ││4 5│││└────────┴────────┴────────┘│
106
+ │ ││ ││6 7│││ │
107
+ │ ││ ││8 9│││┌────────┬────────┬────────┐│
108
+ │ ││ │└───┘│││0.166667│0.142857│0.125 ││
109
+ │ │└──┴─────┘│├────────┼────────┼────────┤│
110
+ │ │ ││0.111111│0.1 │0.090909││
111
+ │ │ │└────────┴────────┴────────┘│
112
+ └─────┴──────────┴────────────────────────────┘
113
+ ```
114
+
115
+ ## Easily Customisable
116
+
117
+ Everything is in Python. Adding new primitives is easy.
118
+
119
+ Update the `primitives.py` file with your new part of speech (e.g. a new verb such as `+::`). Write your implementation of this new part of speech in the relevant executor module (e.g. `verbs.py`) and then update the name-to-method mapping at the foot of that module. That's all that's needed.
120
+
121
+ ## Alternative Executors
122
+
123
+ Execution of sentences is backed by NumPy by default.
124
+
125
+ However Jinx is designed so that it's possible to implement the primitives using alternative frameworks too. Python many Machine Learning and Scientific Programming libraries that could be used to execution J code.
126
+
127
+ To prove this concept, there's _highly experimental and incomplete_ support for [JAX](https://docs.jax.dev/en/latest/index.html):
128
+ ```sh
129
+ jinx --executor jax
130
+ ```
131
+ Primitive verbs are JIT compiled and execute on JAX arrays:
132
+ ```j
133
+ mean =: +/ % #
134
+ mean 33 55 77 100 101
135
+ 73.2
136
+ ```
137
+
138
+ ## Warning
139
+
140
+ This project is experimental. There will be bugs, missing features and performance quirks.
141
+
142
+ Many key parts of J are not currently implemented (but might be in future). These include:
143
+ - Differences in how names are interpreted and resolved at execution time.
144
+ - Locales.
145
+ - Definitions and direct definitions (using `{{ ... }}`).
146
+ - Array types other than floats, integers and and strings.
147
+ - Executing J scripts.
148
+ - Control words.
jjinx-0.0.1/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # Jinx
2
+
3
+ ![ci](https://github.com/ajcr/jinx/actions/workflows/ci.yaml/badge.svg?branch=main)
4
+
5
+ An experimental interpreter for the J programming language, built on top of [NumPy](https://numpy.org/).
6
+
7
+ Implements many of J's primitives and tacit programming capabilities, and can be extended to support execution via other frameworks too.
8
+
9
+ ## Executing J
10
+
11
+ Start the interactive shell:
12
+ ```sh
13
+ jinx
14
+ ```
15
+ The shell prompt is four spaces, so commands appear indented. Internally, all multidimensional arrays are NumPy arrays. Verbs, conjunctions and adverbs are a mixture of Python and NumPy methods.
16
+
17
+ Here are some examples what Jinx can do so far:
18
+
19
+ - Solve the "trapping rainwater" problem (solution taken from [here](https://mmapped.blog/posts/04-square-joy-trapped-rain-water)):
20
+ ```j
21
+ +/@((>./\ <. >./\.)-]) 0 1 0 2 1 0 1 3 2 1 2 1
22
+ 6
23
+ ```
24
+ - Compute the correlation between two arrays of numbers (taken from [here](https://stackoverflow.com/a/44845495/3923281)). This is a complex combination of different verbs, adverbs, conjunctions and trains:
25
+ ```j
26
+ 2 1 1 7 9 (+/@:* % *&(+/)&.:*:)&(- +/%#) 6 3 1 5 7
27
+ 0.721332
28
+ ```
29
+ - Create identity matrices in inventive ways (see [this essay](https://code.jsoftware.com/wiki/Essays/Identity_Matrix)):
30
+ ```j
31
+ |.@~:\ @ ($&0) 3
32
+ 1 0 0
33
+ 0 1 0
34
+ 0 0 1
35
+
36
+ (i.@,~ = >: * i.) 3
37
+ 1 0 0
38
+ 0 1 0
39
+ 0 0 1
40
+
41
+ ((={:)\ @ i.) 3
42
+ 1 0 0
43
+ 0 1 0
44
+ 0 0 1
45
+ ```
46
+ - Solve the Josephus problem (see [this essay](https://code.jsoftware.com/wiki/Essays/Josephus_Problem)). Calculate the survivor's number for a circle of people of size N. Note the use of verb obverse and the rank conjunction:
47
+ ```j
48
+ (1&|.&.#:)"0 >: i. 5 10 NB. N ranges from 1 to 50 here (arranged as a table)
49
+ 1 1 3 1 3 5 7 1 3 5
50
+ 7 9 11 13 15 1 3 5 7 9
51
+ 11 13 15 17 19 21 23 25 27 29
52
+ 31 1 3 5 7 9 11 13 15 17
53
+ 19 21 23 25 27 29 31 33 35 37
54
+ ```
55
+ - Build nested boxes containing heterogeneous data types and print the contents:
56
+ ```j
57
+ (<<'abc'),(<(<'de',.'fg'),(<<i. 5 2)),(<(<"0 ] % i. 2 2 3))
58
+ ┌─────┬──────────┬────────────────────────────┐
59
+ │┌───┐│┌──┬─────┐│┌────────┬────────┬────────┐│
60
+ ││abc│││df│┌───┐│││_ │1 │0.5 ││
61
+ │└───┘││eg││0 1│││├────────┼────────┼────────┤│
62
+ │ ││ ││2 3││││0.333333│0.25 │0.2 ││
63
+ │ ││ ││4 5│││└────────┴────────┴────────┘│
64
+ │ ││ ││6 7│││ │
65
+ │ ││ ││8 9│││┌────────┬────────┬────────┐│
66
+ │ ││ │└───┘│││0.166667│0.142857│0.125 ││
67
+ │ │└──┴─────┘│├────────┼────────┼────────┤│
68
+ │ │ ││0.111111│0.1 │0.090909││
69
+ │ │ │└────────┴────────┴────────┘│
70
+ └─────┴──────────┴────────────────────────────┘
71
+ ```
72
+
73
+ ## Easily Customisable
74
+
75
+ Everything is in Python. Adding new primitives is easy.
76
+
77
+ Update the `primitives.py` file with your new part of speech (e.g. a new verb such as `+::`). Write your implementation of this new part of speech in the relevant executor module (e.g. `verbs.py`) and then update the name-to-method mapping at the foot of that module. That's all that's needed.
78
+
79
+ ## Alternative Executors
80
+
81
+ Execution of sentences is backed by NumPy by default.
82
+
83
+ However Jinx is designed so that it's possible to implement the primitives using alternative frameworks too. Python many Machine Learning and Scientific Programming libraries that could be used to execution J code.
84
+
85
+ To prove this concept, there's _highly experimental and incomplete_ support for [JAX](https://docs.jax.dev/en/latest/index.html):
86
+ ```sh
87
+ jinx --executor jax
88
+ ```
89
+ Primitive verbs are JIT compiled and execute on JAX arrays:
90
+ ```j
91
+ mean =: +/ % #
92
+ mean 33 55 77 100 101
93
+ 73.2
94
+ ```
95
+
96
+ ## Warning
97
+
98
+ This project is experimental. There will be bugs, missing features and performance quirks.
99
+
100
+ Many key parts of J are not currently implemented (but might be in future). These include:
101
+ - Differences in how names are interpreted and resolved at execution time.
102
+ - Locales.
103
+ - Definitions and direct definitions (using `{{ ... }}`).
104
+ - Array types other than floats, integers and and strings.
105
+ - Executing J scripts.
106
+ - Control words.
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "jjinx"
7
+ version = "0.0.1"
8
+ description = "Interpreter for the J programming language."
9
+ readme = "README.md"
10
+ authors = [{ name = "Alex Riley" }]
11
+ license = { file = "LICENSE" }
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Topic :: Scientific/Engineering :: Mathematics",
15
+ "Topic :: Utilities",
16
+ "Typing :: Typed",
17
+ "Intended Audience :: Science/Research",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3.13",
20
+ ]
21
+ keywords = ["J", "interpreter"]
22
+ requires-python = ">=3.13"
23
+ dependencies = [
24
+ "numpy>=2.3.3",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/ajcr/jinx"
29
+
30
+ [project.scripts]
31
+ jinx = "jinx.shell:main"
32
+
33
+ [tool.pytest.ini_options]
34
+ pythonpath = [
35
+ "src"
36
+ ]
37
+ addopts = [
38
+ "--import-mode=importlib",
39
+ ]
40
+
41
+ [dependency-groups]
42
+ dev = [
43
+ "mypy>=1.18.2",
44
+ "pytest>=8.4.2",
45
+ "ruff>=0.13.1",
46
+ ]
47
+ jax = [
48
+ "jax>=0.7.2",
49
+ ]
jjinx-0.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,41 @@
1
+ """J errors for incorrect usage of J primitives."""
2
+
3
+
4
+ class BaseJError(Exception):
5
+ pass
6
+
7
+
8
+ class LengthError(BaseJError):
9
+ pass
10
+
11
+
12
+ class DomainError(BaseJError):
13
+ pass
14
+
15
+
16
+ class ValenceError(BaseJError):
17
+ pass
18
+
19
+
20
+ class JIndexError(BaseJError):
21
+ pass
22
+
23
+
24
+ class SpellingError(BaseJError):
25
+ pass
26
+
27
+
28
+ class StackError(BaseJError):
29
+ pass
30
+
31
+
32
+ class EvaluationError(BaseJError):
33
+ pass
34
+
35
+
36
+ class JSyntaxError(BaseJError):
37
+ pass
38
+
39
+
40
+ class JinxNotImplementedError(BaseJError):
41
+ """Raised when a feature is not implemented in Jinx."""
@@ -0,0 +1,60 @@
1
+ from dataclasses import dataclass
2
+ from typing import Callable
3
+
4
+ from jinx.vocabulary import Adverb, Conjunction, Noun, Verb
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class Executor[T]:
9
+ apply_monad: Callable[[Verb[T], Noun[T]], Noun[T]]
10
+ """Apply monadic form of verb to a noun."""
11
+
12
+ apply_dyad: Callable[[Verb[T], Noun[T], Noun[T]], Noun[T]]
13
+ """Apply dyadic form of verb to two nouns."""
14
+
15
+ apply_conjunction: Callable[
16
+ [Verb[T] | Noun[T], Conjunction, Verb[T]], Verb[T] | Noun[T]
17
+ ]
18
+ """Apply conjunction to left and right arguments."""
19
+
20
+ apply_adverb: Callable[[Verb[T] | Noun[T], Adverb], Verb[T] | Noun[T]]
21
+ """Apply adverb to left argument."""
22
+
23
+ build_fork: Callable[[Noun[T] | Verb[T], Verb[T], Verb[T]], Verb[T]]
24
+ """Build fork."""
25
+
26
+ build_hook: Callable[[Verb[T], Verb[T]], Verb[T]]
27
+ """Build hook."""
28
+
29
+ ensure_noun_implementation: Callable[[Noun[T]], None]
30
+ """Ensure that the noun has an implementation."""
31
+
32
+ primitive_verb_map: dict[
33
+ str, tuple[Callable[[T], T] | None, Callable[[T, T], T] | None]
34
+ ]
35
+ """Map of primitive verb names to implementations of monad and dyad functions."""
36
+
37
+ primitive_adverb_map: dict[str, Callable[[Verb[T]], Verb[T]]]
38
+ """Map of primitive adverb names to implementation function."""
39
+
40
+ primitive_conjuction_map: dict[
41
+ str, Callable[[Verb[T] | Noun[T], Verb[T] | Noun[T]], Verb[T]]
42
+ ]
43
+ """Map of primitive conjunction names to implementation function."""
44
+
45
+ noun_to_string: Callable[[Noun[T]], str]
46
+ """Convert a noun to a string representation for printing."""
47
+
48
+
49
+ def load_executor(name: str) -> Executor:
50
+ if name == "numpy":
51
+ from jinx.execution.numpy import executor as numpy_executor
52
+
53
+ return numpy_executor
54
+
55
+ if name == "jax":
56
+ from jinx.execution.jax import executor as jax_executor
57
+
58
+ return jax_executor
59
+
60
+ raise ValueError(f"Unknown executor: {name}")
@@ -0,0 +1,45 @@
1
+ import jax
2
+ from jinx.errors import JinxNotImplementedError
3
+ from jinx.execution.executor import Executor
4
+ from jinx.execution.jax.adverbs import ADVERB_MAP
5
+ from jinx.execution.jax.application import (
6
+ apply_adverb,
7
+ # apply_conjunction,
8
+ apply_dyad,
9
+ apply_monad,
10
+ build_fork,
11
+ # build_hook,
12
+ ensure_noun_implementation,
13
+ )
14
+ from jinx.execution.jax.verbs import VERB_MAP
15
+
16
+ # from jinx.execution.numpy.conjunctions import CONJUNCTION_MAP
17
+ # from jinx.execution.numpy.conversion import ensure_noun_implementation
18
+ from jinx.execution.numpy.printing import noun_to_string
19
+
20
+ jax.config.update("jax_dynamic_shapes", True)
21
+
22
+
23
+ def make_not_implemented(name: str):
24
+ def _not_implemented(*_, **__):
25
+ raise JinxNotImplementedError(
26
+ f"{name}: not yet implemented in the JAX executor."
27
+ )
28
+
29
+ return _not_implemented
30
+
31
+
32
+ executor = Executor[jax.Array](
33
+ apply_monad=apply_monad,
34
+ apply_dyad=apply_dyad,
35
+ apply_conjunction=make_not_implemented("conjunction"),
36
+ apply_adverb=apply_adverb,
37
+ build_fork=build_fork,
38
+ build_hook=make_not_implemented("hook"),
39
+ ensure_noun_implementation=ensure_noun_implementation,
40
+ primitive_verb_map=VERB_MAP,
41
+ primitive_adverb_map=ADVERB_MAP,
42
+ primitive_conjuction_map={},
43
+ # Just use the NumPy implementation of printing.
44
+ noun_to_string=noun_to_string, # type: ignore[arg-type]
45
+ )
@@ -0,0 +1,91 @@
1
+ """Methods implementing J adverbs."""
2
+
3
+ import functools
4
+ from typing import Callable
5
+
6
+ import jax
7
+ import jax.numpy as jnp
8
+ from jinx.errors import JinxNotImplementedError, ValenceError
9
+ from jinx.execution.jax.application import _apply_dyad
10
+ from jinx.execution.numpy.helpers import (
11
+ maybe_parenthesise_verb_spelling,
12
+ )
13
+ from jinx.vocabulary import Dyad, Monad, Verb
14
+
15
+ INFINITY = float("inf")
16
+
17
+
18
+ def slash_adverb(verb: Verb[jax.Array]) -> Verb[jax.Array]:
19
+ if verb.dyad is None or verb.dyad.function is None:
20
+ # Note: this differs from J which still allows the adverb to be applied
21
+ # to a verb, but may raise an error when the new verb is applied to a noun
22
+ # and the verb has no dyadic valence.
23
+ raise ValenceError(f"Verb {verb.spelling} has no dyadic valence.")
24
+
25
+ if isinstance(verb.dyad.function, jnp.ufunc) and verb.dyad.is_commutative:
26
+ monad = verb.dyad.function.reduce
27
+ dyad = verb.dyad.function.outer
28
+
29
+ else:
30
+ # Slow path: dyad is not a ufunc.
31
+ # The function is either callable, in which cases it is applied directly,
32
+ # or a Verb object that needs to be applied indirectly with _apply_dyad().
33
+ if isinstance(verb.dyad.function, Verb):
34
+ func = functools.partial(_apply_dyad, verb) # type: ignore[assignment]
35
+ else:
36
+ func = verb.dyad.function # type: ignore[assignment]
37
+
38
+ def _dyad_arg_swap(x: jax.Array, y: jax.Array) -> jax.Array:
39
+ return func(y, x)
40
+
41
+ def _reduce(y: jax.Array) -> jax.Array:
42
+ y = jnp.atleast_1d(y)
43
+ y = jnp.flip(y, axis=0)
44
+ return functools.reduce(_dyad_arg_swap, y)
45
+
46
+ monad = _reduce # type: ignore[assignment]
47
+ dyad = NotImplemented
48
+
49
+ spelling = maybe_parenthesise_verb_spelling(verb.spelling)
50
+ spelling = f"{verb.spelling}/"
51
+
52
+ return Verb[jax.Array](
53
+ name=spelling,
54
+ spelling=spelling,
55
+ monad=Monad(name=spelling, rank=INFINITY, function=monad),
56
+ dyad=Dyad(
57
+ name=spelling, left_rank=INFINITY, right_rank=INFINITY, function=dyad
58
+ ),
59
+ )
60
+
61
+
62
+ def bslash_adverb(verb: Verb[jax.Array]) -> Verb[jax.Array]:
63
+ # Common cases that have a straightforward optimisation.
64
+ SPECIAL_MONAD = {
65
+ "+/": jnp.cumulative_sum,
66
+ "*/": jnp.cumulative_prod,
67
+ }
68
+
69
+ if verb.spelling in SPECIAL_MONAD:
70
+ monad_ = SPECIAL_MONAD[verb.spelling]
71
+
72
+ else:
73
+ raise JinxNotImplementedError(
74
+ f"Adverb \\ applied to verb {verb.spelling} is not yet implemented."
75
+ )
76
+
77
+ spelling = maybe_parenthesise_verb_spelling(verb.spelling)
78
+ spelling = f"{spelling}\\"
79
+
80
+ return Verb(
81
+ name=spelling,
82
+ spelling=spelling,
83
+ monad=Monad(name=spelling, rank=INFINITY, function=monad_),
84
+ dyad=Dyad(name=spelling, left_rank=0, right_rank=INFINITY, function=None), # type: ignore[arg-type]
85
+ )
86
+
87
+
88
+ ADVERB_MAP: dict[str, Callable[[Verb[jax.Array]], Verb[jax.Array]]] = {
89
+ "SLASH": slash_adverb,
90
+ "BSLASH": bslash_adverb,
91
+ }