This snipped shows different ways of bindings. The...
# mathematics
a
This snipped shows different ways of bindings. The first example shows standard eager binding( forward declaration. It could be made in a type safe way as shown in the original file. The second example shows lazy binding via expression API. Againg, you thoghts are really appreciated.
b
I found these examples a little confusing. What does
val x = bind(x)
mean, how can you bind
x
before it is declared? Or is this just a name shadowing issue? It seems like there are some captured variables from outside the lambda scope being brought inside and rebound:
Copy code
val x by symbol
val y by symbol
val res: Double = diff(3, x to 1.0, y to 1.0) {
  val x = bind(x)//by binding()
  val y = symbol("y")
  val z = x * (-sin(x * y) + y)
  z.derivative(x)
}
When you say,
The binding is unavoidable since many operations (like autodiffs) require forward variable declaration.
I don't understand why
val x = bind(x)
is necessary to write inside the lambda, it seems like it should be possible to infer this from the context, or pass into the lambda through a parameter. Also, why does
diff(3, x to 1.0, y to 1.0)
have three arguments in the above example? If possible, I would try to get this form somehow:
Copy code
val x by symbol
val y by symbol
val res: Double = diff(x to 1.0, y to 1.0) { x, y ->
  val z = x * (-sin(x * y) + y)
  z.derivative(x)
}
Is it necessary to wrap everything inside a
diff {...}
context or is there a way to hide this inside the call to
.derivative(x)
)? Ideally, it ought to be possible to avoid the wrapper altogether:
Copy code
val x by symbol
val y by symbol
val z = x * (-sin(x * y) + y)
val res = z.autoDiff(x to 3.0, y to 1.0) // or alternately z.autoDiff(x)(x to 3.0, y to 1.0)
Although I can also understand if this syntax is not possible for API design reasons. In the second example, if you see
val expr = diff{val x = bind(x); x + x}
alone, it is not obvious what
diff{ ... }
without any variables is supposed to mean. If we later take
diff {...}.derivative(x)
is this the second derivative? Maybe using
diff(x to 1.0, y to 2.0) { ... }
upfront make more sense.
a
It is indeed a bit confusing, but I did not find a way around it. The thing is that
x
outside the context and
x
bound inside the context are two different x from the point of view of the program. The first one is
Symbol
, the last one is for example
SimpleAutoDiffValue
. We can't operate with
Symbol
s because they are not bound to context and do not hold forward declared values.
As for wrappers, it is how kmath works, we do not want to declare global behaviors for objects, we want those behaviors to be encapsulated in specific context. For example, your code could not work since the specific type of
z
and algorithm used to produce it would depend on what library do you use for it. If we, for example, say that operations on symbols would produce commons-math autodiff expressions, I would not be able to use kotlingrad or other symbolic operations. The meaning of
diff
and
dx
could be seen from the full test listing: https://github.com/mipt-npm/kmath/blob/feature/diff-api/kmath-core/src/commonTest/kotlin/kscience/kmath/expressions/SimpleAutoDiffTest.kt. They are just type-safe wrappers for the general API
👍 1
It is possible to add auto-binding to the context meaning that when one encounters an unbound symbol ( like x), he will automatically try to bind it to the context. It will clean counter-intuitive
bind
operations, but it would require to diplicate all operations in the context for
Symbol
as well. Actually, it is not so hard to do. And it could be added in a non-breaking way, so I will possibly try to do it. Sadly, I won't be able to just add those operations as extensions due to lack of multi-receivers, otherwise I would just write
[ExpressionAlgebra, Symbol].plus()
extensions.
👍 1