Hi all,
In response to a request by Poul Nielsen, I have produced a model
demonstrating how this functionality might be used.
type_library.cellml is the beginnings of a type library, which provides
a complex number type and some other types needed to support it. The
complex number type is actually a function which takes a 'base type' and
returns a type. For example, if you wanted to represent a 'vector' in a
plane in metres as a complex number, you can call the function which
returns a type with real_metres as the argument to get a type for
storing a number of metres. This form implicitly requires that both
cartesian co-ordinates have the same type.
Also included is a library of utilities for working with these complex
numbers to get the cartesian and polar co-ordinates.
Writing this up has identified several places where there is room for
improvement:
=> We require that the type attribute is present on variables in every
component it is used (not just one of them), and these types are also
variables which must be present everywhere. If types are all of type
type, this isn't as bad, because it means that we only require one extra
variable. However, if we only required that the type attribute was
present in one of the connected network of variables (but allowed it to
be present in more as long as the types match) we would reduce the
burden on model authors.
=> Wildcard types, or even some sort of type inheritance scheme once
more looks quite helpful. For example, we have a function called 'sign'
which returns 1, 0, or -1 depending on the sign of the input. But we
have to explicitly say what the type of the input is (not just 'any kind
of real number' for instance). There will be many similar cases.
Sometimes this is a good thing as it provides additional type safety,
but in other cases, it is bad. We could have a unidirectional concept of
compatibility as used in inheritance hierarchies (so for example,
real_metre matches real_anything, but real_anything doesn't match
real_metre, since real_metre inherits from real_anything). This can be
worked around by using functions which return types, or by using the
trick we used in type_utilities of having a variable you connect up to
correct type, but the cost of having one import or defining a type just
to call a basic function puts a lot of burden on model authors and makes
models rather verbose.
Best wishes,
Andrew
<c11:model xmlns:c11="http://www.cellml.org/cellml/1.1#"
xmlns:c12="http://www.cellml.org/cellml/1.2#"
xmlns:m="http://www.w3.org/1998/Math/MathML">
<c11:component name="TypeLibrary">
<!-- type_from_type_function_type: The type of a function which takes a
type and returns a type. -->
<c12:variable name="type_from_type_function_type" type="type"
public_interface="yes" />
<!-- cartesian_complex_type_function: A function which converts a scalar
type (parameter)
into the type of the cartesian form of the complex number as a
vector of two reals
having the type of the parameter. -->
<c12:variable name="complex_type_function"
type="type_from_type_function_type" public_interface="yes"/>
<!-- The following variables are used locally for bvars and the like
only... -->
<c12:variable name="base_type" type="type" />
<m:math>
<m:apply id="type_from_type_function_eqn"><m:eq/>
<m:ci>type_from_type_function</m:ci>
<m:apply><c12:function_type />
<m:ci>type</m:ci>
<m:ci>type</m:ci>
</m:apply>
</m:apply>
<!-- We might want to build this into CellML 1.2 core (with the option
that secondary specification don't support it) but if not, this is
how complex numbers in their cartesian form could be defined. We
probably should
-->
<m:apply id="cartesian_complex_type_function_eqn"><m:eq/>
<m:ci>complex_type_function</m:ci>
<m:lambda>
<m:bvar><m:ci>base_type</m:ci></m:bvar>
</m:lambda>
<m:apply><c12:vector_type />
<!-- First argument: type of vector elements... -->
<m:ci>base_type</m:ci>
<!-- Second argument: size of vector -->
<m:cn c12:type="real_dimensionless">2</m:cn>
</m:apply>
</m:apply>
</m:math>
</c11:component>
</c11:model>
<c11:model xmlns:c11="http://www.cellml.org/cellml/1.1#"
xmlns:c12="http://www.cellml.org/cellml/1.2#"
xmlns:m="http://www.w3.org/1998/Math/MathML"
xmlns:xlink="http://www.w3.org/1999/xlink">
<c11:component name="ComplexUtilities">
<!-- Connect up to the type you want this component to work with. -->
<c12:variable name="base_type" type="type" public_interface="yes"/>
<!-- The complex_type (can be computed from base_type). -->
<c12:variable name="complex_type" type="type" public_interface="yes" />
<c12:variable name="complex_to_base_function_type" type="type"
public_interface="yes" />
<!-- A function for fetching cartesian X co-ordinates. -->
<c12:variable name="complex_get_cartesian_x"
type="complex_to_base_function_type" public_interface="yes" />
<c12:variable name="type_from_type_function_type" type="type"
private_interface="yes" />
<c12:variable name="complex_type_function"
type="type_from_type_function_type" private_interface="yes"/>
<!-- Local types -->
<c12:variable name="base_type_to_dimensionless_function_type" type="type" />
<c12:variable name="base_type_base_type_to_radians_function_type"
type="type" />
<!-- Local functions -->
<c12:variable name="sign" type="base_type_to_dimensionless_function_type" />
<c12:variable name="arctan2"
type="base_type_base_type_to_radians_function_type" />
<!-- Local bound variables -->
<c12:variable name="complex" type="complex_type" />
<c12:variable name="x" type="base_type" />
<c12:variable name="y" type="base_type" />
<m:math>
<!-- An example of a units computation... -->
<m:apply><m:eq/>
<m:ci>complex_type</m:ci>
<m:apply><m:ci>complex_type_function</m:ci>
<m:ci>base_type</m:ci>
</m:apply>
</m:apply>
<m:apply><m:eq/>
<m:ci>complex_to_base_function_type</m:ci>
<m:apply><c12:function_type/>
<m:ci>base_type</m:ci>
<m:ci>complex_type</m:ci>
</m:apply>
</m:apply>
<m:apply><m:eq/>
<m:ci>complex_get_real</m:ci>
<m:lambda>
<m:bvar><m:ci>complex</m:ci></m:bvar>
<m:apply><c12:vector_element/>
<m:ci>complex</m:ci>
<m:cn c12:type="real_dimensionless">0</m:cn>
</m:apply>
</m:lambda>
</m:apply>
<m:apply><m:eq/>
<m:ci>complex_get_imaginary</m:ci>
<m:lambda>
<m:bvar><m:ci>complex</m:ci></m:bvar>
<m:apply><c12:vector_element/>
<m:ci>complex</m:ci>
<m:cn c12:type="real_dimensionless">1</m:cn>
</m:apply>
</m:lambda>
</m:apply>
<m:apply><m:eq/>
<m:ci>complex_get_polar_modulus</m:ci>
<m:lambda>
<m:bvar><m:ci>complex</m:ci></m:bvar>
<m:apply><m:root/>
<m:apply><m:plus/>
<m:apply><m:pow/>
<m:apply><m:ci>complex_get_real</m:ci>
<m:ci>complex</m:ci>
</m:apply>
<m:cn c12:type="real_dimensionless">2</m:cn>
</m:apply>
<m:apply><m:pow/>
<m:apply><m:ci>complex_get_imaginary</m:ci>
<m:ci>complex</m:ci>
</m:apply>
<m:cn c12:type="real_dimensionless">2</m:cn>
</m:apply>
</m:apply>
</m:apply>
</m:lambda>
</m:apply>
<m:apply><m:eq/>
<m:ci>base_type_to_dimensionless_function_type</m:ci>
<m:apply><c12:function_type/>
<m:ci>real_dimensionless</m:ci>
<m:ci>base_type</m:ci>
</m:apply>
</m:apply>
<m:apply><m:eq/>
<m:ci>base_type_base_type_to_radians_function_type</m:ci>
<m:apply><c12:function_type/>
<m:ci>real_radians</m:ci>
<m:ci>base_type</m:ci>
<m:ci>base_type</m:ci>
</m:apply>
</m:apply>
<!-- This is a useful function; we could put it in another library. -->
<m:apply><m:eq/>
<m:ci>sign</m:ci>
<m:lambda>
<m:bvar>x</m:bvar>
<m:piecewise>
<m:piece>
<m:cn c12:type="real_dimensionless">0</m:cn>
<m:apply><m:eq/>
<m:ci>x</m:ci>
<m:cn c12:type="base_type">0</m:cn>
</m:apply>
</m:piece>
<m:piece>
<m:cn c12:type="real_dimensionless">-1</m:cn>
<m:apply><m:lt/>
<m:ci>x</m:ci>
<m:cn c12:type="base_type">0</m:cn>
</m:apply>
</m:piece>
<m:otherwise>
<m:cn c12:type="real_dimensionless">1</m:cn>
</m:otherwise>
</m:piecewise>
</m:lambda>
</m:apply>
<m:apply><m:eq/>
<m:ci>arctan2</m:ci>
<m:lambda>
<m:bvar><m:ci>y</m:ci></m:bvar>
<m:bvar><m:ci>x</m:ci></m:bvar>
<m:apply><m:times/>
<m:piecewise>
<m:piece>
<m:apply><m:divide/>
<m:pi/>
<m:cn c12:type="real_dimensionless">2</m:cn>
</m:apply>
<m:apply><m:eq/>
<m:ci>x</m:ci>
<m:cn c12:type="base_type">0</m:cn>
</m:apply>
</m:piece>
<m:piece>
<m:apply><m:arctan/>
<m:apply><m:abs/>
<m:apply><m:divide/>
<m:ci>y</m:ci>
<m:ci>x</m:ci>
</m:apply>
</m:apply>
</m:apply>
<m:apply><m:gt/>
<m:ci>x</m:ci>
<m:cn c12:type="base_type">0</m:cn>
</m:apply>
</m:piece>
<m:otherwise>
<m:apply><m:minus/>
<m:pi />
<m:apply><m:arctan/>
<m:apply><m:abs/>
<m:apply><m:divide/>
<m:ci>y</m:ci>
<m:ci>x</m:ci>
</m:apply>
</m:apply>
</m:apply>
</m:apply>
</m:otherwise>
</m:piecewise>
<m:apply><m:ci>sign</m:ci>
<m:ci>y</m:ci>
</m:apply>
</m:apply>
</m:lambda>
</m:apply>
<m:apply><m:eq/>
<m:ci>complex_get_polar_argument</m:ci>
<m:lambda>
<m:bvar><m:ci>complex</m:ci></m:bvar>
<m:apply><m:ci>arctan2</m:ci>
<m:apply><m:ci>complex_get_cartesian_y</m:ci>
<m:ci>complex</m:ci>
</m:apply>
<m:apply><m:ci>complex_get_cartesian_x</m:ci>
<m:ci>complex</m:ci>
</m:apply>
</m:apply>
</m:lambda>
</m:apply>
</m:math>
</c11:component>
<c11:import xlink:href="type_library.cellml">
<c11:component name="TypeLibrary" component_ref="TypeLibrary"/>
</c11:import>
<c12:encapsulation>
<c11:component_ref component_ref="ComplexUtilities">
<c11:component_ref component_ref="TypeLibrary"/>
</c11:component_ref>
</c12:encapsulation>
<c12:connection component_1="TypeLibrary" component_2="ComplexUtilities">
<c11:map_variables variable_1="type_from_type_function_type"
variable_2="type_from_type_function_type" />
<c11:map_variables variable_1="complex_type_function"
variable_2="complex_type_function" />
</c12:connection>
</c11:model>
_______________________________________________
cellml-discussion mailing list
cellml-discussion@cellml.org
http://www.cellml.org/mailman/listinfo/cellml-discussion