|
|
last edited 2 years ago by test1 |
1 2 | ||
Editor: test1
Time: 2022/06/10 17:08:15 GMT+0 |
||
Note: |
changed: -D. Cyganski - July 11-13, 2007 Originally by D. Cyganski - July 11-13, 2007 changed: -after the fact: -coercion operates "on the surface and not deeply" to Float after the fact: changed: -However, there is a real need for a deep coercion -operator that operates on the inner most atomic constants! Suppose we define: Actually there is simple workaround: \begin{axiom} cos(2/3)::Expression(Float) \end{axiom} Here result is expression with floating point coefficients, which by default evaluates cosine, so result is effectively a float. More generally, when there is a need for a deep coercion operator that operates on the inner most atomic constants, one needs to specify full type tower, with changed inner type. In such case interpreter usually is able to create needed coercion, even if there is no definition of appropriate 'coerce'. To clarify, suppose we define: changed: -axiom system. The axiom book and online resources are full of examples FriCAS system. The FriCAS book and online resources are full of examples changed: -of this form of expression - even though it amounts to lying to axiom of this form of expression - even though it amounts to lying to FriCAS changed: -But woe to us if we wish later to evaluate it in a more general way -because it is a tangled web we weave when we practice to decieve: If we wish later to evaluate it in a more general way we need appropriate coercion or convertion (which in hard case may require few manual steps). However, one needs to use appropriate types, for example Expression(Float): changed: -Thus in effect once we wrap a function around an Integer base definition, we are -stuck and unable to evaluate it as a float later, unlike the core basic functions that -can be used either way. This forces us to choose the Float type throughout -at a loss of comprehensibility and analyzability, unless we seek to more than double -our development type by supplying an overloaded Integer base and Float base -version of *every step* of a sequential development of a formula. - -Bizarrely, the draw function seems to have the power to override the type -problem as shown here! But coercion to Expression(Float) works: \begin{axiom} cosf(2/3)::Expression(Float) \end{axiom} If one needs Float as result type, it is a bit more complicated: \begin{axiom} retract(cosf(2/3)::Expression(Float))@Float \end{axiom} Here retract forgets about expression type and gives us a float. In some case, in particular in the draw function appropriate convertions are done automatically, to allow floating point evaluation: changed: -Why can't we grant this deep coercion power to some new form of floating -point conversion operation which can be applied at will??? If draw has -this power, why not put it in the hands of the user? - -Alternatively, Actually draw produces compiled function, similar to example in GeneratingCompiledFunctions Some users think that changed: -while in Axiom we get while in FriCAS we get changed: - -In a way, Axiom already has a quantity treated like this - the constant However, such type would be quite different from current FriCAS Expression. Also, symbolic operations on floating point values are problematic (in floating point we only have approximate result, so can not decide true equality). Keeping distinction between float and integers would significantly limit possible simplifications. Anyway, nobody wrote such a domain for FriCAS... At first glance it looks that FriCAS already has a quantity with "mixed rule" - the constant added: However, this is done by giving %pi a special type, called Pi. Simple operations preserve this type, but ultimately it is converted to some other type, usually Expression(Integer) or Float changed: -act normally, for example, no complaint from axiom with act normally, for example, no complaint from FriCAS with changed: -Obiously the quaternion version of "/" is being invoked despite mismatches Obviously the quaternion version of "/" is being invoked despite mismatches changed: -Well, what if we built a new cosine function that forced the form of -of the arguments into certain types to avoid the mismatch? -\begin{axiom} -c(y:Float):Float==cos(y) -\end{axiom} -At first this seems to work, we can still evaluate a float -\begin{axiom} -c(1.237) -\end{axiom} -and we can even get a float answer when we introduce the integer -coercable biquat variable value generated above!!! -\begin{axiom} -c(x) -\end{axiom} -But that was only misdirection, because this breaks down for reasonable expressions -because of the "/" operation still not being resolved correctly. -\begin{axiom} -c(1.237/2) -\end{axiom} - -Rather than complaining about it, what if we tried the various coercions that -served to solve the similar type conversion problem we had when just dealing -with Integer Fraction versus Floats at the top of this page. Our results are mixed! -Recall that each of the following worked in the previous case, producing the correct -floating result in each case: -\begin{axiom} -cos(2/3::Float) -cos((2/3)::Float) -cos(2/3$Float) -cos((2/3)$Float) -cos(2/3@Float) -cos((2/3)@Float) - -\end{axiom} -Try these examples with our type constrained function, which has better luck now -\begin{axiom} -c(2/3::Float) -c((2/3)::Float) -c(2/3$Float) -c((2/3)$Float) -c(2/3@Float) -c((2/3)@Float) -\end{axiom} - -Could the above problems been avoided by not assigning types to the -function we defined? Let's repeat the entire above example with this -single change for the function c2 -\begin{axiom} -c2(y)==cos(y) -c2(1.237) -c2(x) -\end{axiom} -But that was only misdirection, because this breaks down for reasonable expressions -\begin{axiom} -c2(1.237/2) -\end{axiom} -and various attempts at coercion also fail-compare these results to the -previous ones -\begin{axiom} -c2(2/3::Float) -c2((2/3)::Float) -c2(2/3$Float) -c2((2/3)$Float) -c2(2/3@Float) -c2((2/3)@Float) - -\end{axiom} -Lastly, we cannot now use the graph function, draw, on such a function -since the wrong / function is used, contrary to the bypassing of internal -types we saw take place with draw in the example prior to the introduction -of operator overloading -\begin{axiom} -draw(c(x),x=0..15) - -\end{axiom} -*Not safe at any speed:* -Most oddly, the ordinary cos() function which exposes no "/" division -Now fails to work with draw despite the fact that we just saw it above -still working with Integer and Float arguments applied directly! -\begin{axiom} -draw(cos(x),x=0..15) -\end{axiom} - Actually, problem here is that "/" is defined in interpreter and interpreter does not allow overloading for user functions. To avoid "capture" of names reusable functions must be defined in Spad files (and some care is needed to structure algebra files to avoid unexpected choices). In interpreter one can use domain qualification to avoid unwanted choice: \begin{axiom} cos((15.457/6)$Float) \end{axiom} Or in prefix form \begin{axiom} cos((/$Float())(15.457, 6)) \end{axiom} Note that even if arguments are of exact types interpreter still uses user-defined function \begin{axiom} cos(15.457 / 6::Float) \end{axiom} Similarly using @ to request specific type does not help: \begin{axiom} (15.457 / 6)@Float \end{axiom} In general, redefining some core function as user function may lead to trubles in seemingly unrelated places. In compiled files overloading was resolved at compile time, so calls in compiled files are unaffected by user-defined function (they are affected when some of library files are modified and recompiled). But FriCAS library sometimes uses interpreter to define new functions on the fly. If such generated function uses "/", then (with our earlier definition) it will get user-defined version, which typically will cause trouble. In particular, this may affect draw, since it generates function on the fly (but many function are generated in simplified way which uses hardcoded meaning of "/").
Type Conversion and Overloading problems
(Found while developing Biquaternion calculus support function collection)
Originally by D. Cyganski - July 11-13, 2007
Several non-intuitive problems with overloading and type conversions while developing the biquaternion support function collection. I have extracted the minimum code set to illustrate each of these herein.
Implicit and Explicit Type Conversions
We begin by illustrating function calling with variously typed arguments and conversions which we will break in various ways, some understandable, others not(?), below.
The cos function will produce float outcomes for float arguments
(1) -> cos(1.237)
(1) |
can handle expressions that mix floats and integers
cos(1.237/2)
(2) |
but will respect an integer expression, as we would want it too, by not evaluating
cos(2/3)
(3) |
We can coerce the evaluation as a float by forcing the floating point evaluation of the division and typing of the outcome in a variety of ways. Each of the following forms is effective in some appropriate and understandable way. Some act explicitly on the "/" operator to force a polymorphic choice, others convert the type of the second constant in each expression with then results in a proper implicit selection of which "/" definition to use:
cos(2/3::Float)
(4) |
cos((2/3)::Float)
(5) |
cos(2/3$Float)
(6) |
cos((2/3)$Float)
(7) |
cos(2/3@Float)
(8) |
cos((2/3)@Float)
(9) |
But, as we would expect, it is too late to attempt coercion to Float after the fact: as illustrated here
cos(2/3)::Float
Cannot convert the value from type Expression(Integer) to Float .
Actually there is simple workaround:
cos(2/3)::Expression(Float)
(10) |
Here result is expression with floating point coefficients, which by default evaluates cosine, so result is effectively a float.
More generally, when there is a need for a deep coercion
operator that operates on the inner most atomic constants, one
needs to specify full type tower, with changed inner type. In such
case interpreter usually is able to create needed coercion, even
if there is no definition of appropriate coerce
. To
clarify, suppose we define:
cosf(x:Expression Integer):Expression Integer == 1+cos(x/2)
Function declaration cosf : Expression(Integer) -> Expression( Integer) has been added to workspace.
which is an example of a simple function that might be defined in the course of typical work. We wish to declare functions as having Integer based arguments and outcomes because this results in behaviors that preserve our representation of Integer fractions, rather than forming approximate decimal expansions, which is perferred for purposes of analytic examination and simplification for both the human and the FriCAS system. The FriCAS book and online resources are full of examples in which this choice has been made by the authors thanks to the power of this form of expression - even though it amounts to lying to FriCAS in many cases as to the ultimate destiny of the function being defined. If we wish later to evaluate it in a more general way we need appropriate coercion or convertion (which in hard case may require few manual steps). However, one needs to use appropriate types, for example Expression(Float):
cosf(2/3)
Compiling function cosf with type Expression(Integer) -> Expression( Integer)
(11) |
cosf((2/3)::Float)
Conversion failed in the compiled user function cosf .
Cannot convert the value from type Float to Expression(Integer) .
But coercion to Expression(Float) works:
cosf(2/3)::Expression(Float)
(12) |
If one needs Float as result type, it is a bit more complicated:
retract(cosf(2/3)::Expression(Float))@Float
(13) |
Here retract forgets about expression type and gives us a float.
In some case, in particular in the draw function appropriate convertions are done automatically, to allow floating point evaluation:
draw(cosf(x),x=0..15)
Compiling function %C with type DoubleFloat -> DoubleFloat Graph data being transmitted to the viewport manager... FriCAS2D data being transmitted to the viewport manager...
(14) |
Actually draw produces compiled function, similar to example in GeneratingCompiledFunctions
Some users think that it would be best to have a mixed type - mixed = Interger/Float. Like Maple expressions it would leave Integers as integers and floats as floats, unmolested and treated as generic constant quantities will distinguishable parts until an evalf like function that would force them entirely into the Float type. For example, in Maple, "cos(2/3)+1.2323" remains as is, while in FriCAS we get
cos(2/3)+1.2323
(15) |
However, such type would be quite different from current FriCAS Expression. Also, symbolic operations on floating point values are problematic (in floating point we only have approximate result, so can not decide true equality). Keeping distinction between float and integers would significantly limit possible simplifications. Anyway, nobody wrote such a domain for FriCAS...
At first glance it looks that FriCAS already has a quantity with "mixed rule" - the constant %pi is treated as a special float which remains unevaluated and does not force combination of itself with an Integer and simply results in a new kind of Integer expression of type Pi.
3/4+%pi
(16) |
However, this is done by giving %pi a special type, called Pi. Simple operations preserve this type, but ultimately it is converted to some other type, usually Expression(Integer) or Float
Overloading problems
Now let's examine properties and problems with overloading.
Define the type Q of Hamiltonian biquaternions
C:=Complex Expression Integer
(17) |
Q:=Quaternion C
(18) |
While developing the support functions, this definition of biquat division was introduced to simplify the format of the formulae
((x:Q)/(y:Q)):Q == x*inv(y)
Function declaration ?/? : (Quaternion(Complex(Expression(Integer))) ,Quaternion(Complex(Expression(Integer)))) -> Quaternion(Complex (Expression(Integer))) has been added to workspace.
But is this typed function in any way actually restricted to quaternions? On the face, it would appear all is normal, here's an example of integer division
x:=15/6
Compiling function / with type (Quaternion(Complex(Expression( Integer))),Quaternion(Complex(Expression(Integer)))) -> Quaternion(Complex(Expression(Integer)))
(19) |
But though the answer was right, the type is now a biquat. If we don't notice this, and procede, some things seem still to act normally, for example, no complaint from FriCAS with
cos(x)
(20) |
Of course we still get a correct answers with
cos(1.237)
(21) |
But let's try to apply this is a simple mixed float/integer function
cos(15.457/6)
Conversion failed in the compiled user function / .
Cannot convert the value from type Float to Quaternion(Complex( Expression(Integer))) .
Obviously the quaternion version of "/" is being invoked despite mismatches of the arguments and the supposed overloading in effect.
Actually, problem here is that "/" is defined in interpreter and interpreter does not allow overloading for user functions. To avoid "capture" of names reusable functions must be defined in Spad files (and some care is needed to structure algebra files to avoid unexpected choices).
In interpreter one can use domain qualification to avoid unwanted choice:
cos((15.457/6)$Float)
(22) |
Or in prefix form
cos((/$Float())(15.457,6))
(23) |
Note that even if arguments are of exact types interpreter still uses user-defined function
cos(15.457 / 6::Float)
Conversion failed in the compiled user function / .
Cannot convert the value from type Float to Quaternion(Complex( Expression(Integer))) .
Similarly using @ to request specific type does not help:
(15.457 / 6)@Float
Conversion failed in the compiled user function / .
Cannot convert the value from type Float to Quaternion(Complex( Expression(Integer))) .
In general, redefining some core function as user function may lead to trubles in seemingly unrelated places. In compiled files overloading was resolved at compile time, so calls in compiled files are unaffected by user-defined function (they are affected when some of library files are modified and recompiled). But FriCAS library sometimes uses interpreter to define new functions on the fly. If such generated function uses "/", then (with our earlier definition) it will get user-defined version, which typically will cause trouble.
In particular, this may affect draw, since it generates function on the fly (but many function are generated in simplified way which uses hardcoded meaning of "/").