-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathtesseract_api.py
More file actions
120 lines (95 loc) · 3.25 KB
/
Copy pathtesseract_api.py
File metadata and controls
120 lines (95 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# Copyright 2025 Pasteur Labs. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from typing import Any, Self
import equinox as eqx
import jax.numpy as jnp
import numpy as np
from pydantic import BaseModel, Field, model_validator
from tesseract_core.runtime import Array, Differentiable, Float32
from tesseract_core.runtime.jax_recipes import (
jax_abstract_eval,
jax_apply,
jax_jacobian,
jax_jvp,
jax_vjp,
)
class Vector_and_Scalar(BaseModel):
v: Differentiable[Array[(None,), Float32]] = Field(
description="An arbitrary vector"
)
s: Differentiable[Float32] = Field(description="A scalar", default=1.0)
def scale(self) -> np.ndarray:
return self.s * self.v
class InputSchema(BaseModel):
a: Vector_and_Scalar = Field(
description="An arbitrary vector and a scalar to multiply it by"
)
b: Vector_and_Scalar = Field(
description="An arbitrary vector and a scalar to multiply it by "
"must be of same shape as b"
)
norm_ord: int = Field(
description="Order of norm (see numpy.linalg.norm)",
default=2,
)
@model_validator(mode="after")
def validate_shape_inputs(self) -> Self:
if self.a.v.shape != self.b.v.shape:
raise ValueError(
f"a.v and b.v must have the same shape. "
f"Got {self.a.v.shape} and {self.b.v.shape} instead."
)
return self
class Result_and_Norm(BaseModel):
result: Differentiable[Array[(None,), Float32]] = Field(
description="Vector s_a·a + s_b·b"
)
normed_result: Differentiable[Array[(None,), Float32]] = Field(
description="Normalized Vector s_a·a + s_b·b/|s_a·a + s_b·b|"
)
class OutputSchema(BaseModel):
vector_add: Result_and_Norm
vector_min: Result_and_Norm
@eqx.filter_jit
def apply_jit(inputs: dict) -> dict:
a_scaled = inputs["a"]["s"] * inputs["a"]["v"]
b_scaled = inputs["b"]["s"] * inputs["b"]["v"]
add_result = a_scaled + b_scaled
min_result = a_scaled - b_scaled
return {
"vector_add": {
"result": add_result,
"normed_result": add_result
/ jnp.linalg.norm(add_result, ord=inputs["norm_ord"]),
},
"vector_min": {
"result": min_result,
"normed_result": min_result
/ jnp.linalg.norm(min_result, ord=inputs["norm_ord"]),
},
}
def apply(inputs: InputSchema) -> OutputSchema:
"""Multiplies a vector `a` by `s`, and sums the result to `b`."""
return jax_apply(apply_jit, inputs)
def jacobian(
inputs: InputSchema,
jac_inputs: set[str],
jac_outputs: set[str],
):
return jax_jacobian(apply_jit, inputs, jac_inputs, jac_outputs)
def jacobian_vector_product(
inputs: InputSchema,
jvp_inputs: set[str],
jvp_outputs: set[str],
tangent_vector: dict[str, Any],
):
return jax_jvp(apply_jit, inputs, jvp_inputs, jvp_outputs, tangent_vector)
def vector_jacobian_product(
inputs: InputSchema,
vjp_inputs: set[str],
vjp_outputs: set[str],
cotangent_vector: dict[str, Any],
):
return jax_vjp(apply_jit, inputs, vjp_inputs, vjp_outputs, cotangent_vector)
def abstract_eval(abstract_inputs):
return jax_abstract_eval(apply_jit, abstract_inputs)