Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit conversion for generic class restricted only to some types

Assume we have idea for generic class Matrix<T> where T is numeric type (Complex or double or float, int etc).

It is natural that we have implicit conversions in C# from float to double, from double to Complex. General rule is that we have implicit conversion from smaller type to bigger type. So far so good.

Now imagine we are implementing our Matrix<T> type. Since this new type in a way is numeric also (or at least it holds numeric values) it is only natural to have implicit conversions from Matrix<float> to Matrix<double>, from Matrix<double> to Matrix<Complex> etc. At least it is nice to have these for mathematical operations like multiply, add etc. However this seems impossible to implement correctly because implicit operator requires that at least one type is the same as class we are implementing it in.

Example: below code doesn't compile even thought it could resolve my problem.

public abstract partial class Matrix<T>
{
    /// <summary>
    /// Implicitly converts a matrix to double precision complex numbers.
    /// </summary>
    public static implicit operator Matrix<Complex64>(Matrix<double> matrix)
    {
        matrix.ToComplex();
    }

    /// <summary>
    /// Implicitly converts a matrix to double precision real numbers.
    /// </summary>
    public static implicit operator Matrix<double>(Matrix<float> matrix)
    {
        matrix.ToDouble();
    }
}

It will not compile because "CS0556 User-defined conversion must convert to or from the enclosing type" and let's say I'm fine with that because it is part of the language specification but shouldn't there be any other way to achieve this?

For example this doesn't compile either.

public abstract partial class Matrix<double>
{
    /// <summary>
    /// Implicitly converts a matrix to single precision real numbers.
    /// </summary>
    public static implicit operator Matrix<double>(Matrix<float> matrix)
    {
        matrix.ToDouble();
    }
}

Is there any way to achieve this thing, it feels natural so I think it should be achievable?

For now I have created a workaround which enables implicit conversion for all types to biggest one but that doesn't resolve conversion from Matrix<float> to Matrix<double>, it only resolves conversions to Matrix<Complex>.

public abstract partial class Matrix<T>
{
    /// <summary>
    /// Implicitly converts a matrix to double precision complex numbers.
    /// </summary>
    public static implicit operator Matrix<Complex64>(Matrix<T> matrix)
    {
        return matrix.Map(x =>
        {
            if (x is Numerics.Complex32)
            {
                var xc32 = (Numerics.Complex32)(object)x;
                return new Complex64(xc32.Real, xc32.Imaginary);
            }
            return new Complex64(Convert.ToDouble(x), 0);
        }, Zeros.AllowSkip);
    }
}

If anyone is interested in background around this problem, you may take a look at https://github.com/mathnet/mathnet-numerics/issues/304

Another option to resolve this could be to use something like "extension operators" (similar to extension methods) but those are not present in C#.

like image 872
Pawel Troka Avatar asked Oct 29 '22 13:10

Pawel Troka


2 Answers

The only way I know to create a generic vector or matrix class and have it do algebra (addition, subtraction, multiplication) is to emit MSIL codes that invoke operators (ex. the operator +). Then it can be used with any type that has static MyType operator + (MyType a, MyTYpe b) defined as well as the built in types.

See this answer of mine to a similar question for more details.

Using the static methods from in Operation<T> you can have the following example code

public class Matrix<T>
{
    T[,] elements;

    static readonly Func<T,T> add = Operation<T>.Add;

    public static Matrix<T> operator + (Matrix<T> A, Matrix<T> B)
    {
       Matrix<T> result = new Matrix<T>(rows,cols);
       for(int i=0; i<rows; i++)
       {
         for(int j=0; j<cols; j++)
         {
            result[i,j] = add(A[i,j], B[i,j]);
         }
       }
       return result;
    }    
    // rest of algebra
}

So if you do have a Complex64 class defined that has operator + defined you can declare Matrix<Complex64> and do algebra like var C = A+2*B directly. The runtime is going to call the appropriate operator for either built in types or custom types. The speed impact is minimal because the reflection to find the appropriate method to call is only done once per type at first use.

like image 141
John Alexiou Avatar answered Nov 15 '22 04:11

John Alexiou


First of all, I'm not really sure using generics with numeric primitive types is a good path to go down. This is a pretty serious lacking aspect of the language and there doesn't seem to be any plan in addressing it on short notice. Read this SO answer for more info.

  1. There is no numeric constraint, the best you can do is struct which is pretty bad.
  2. For any arithmetic support, which is a must if you are implementing a matrix, you need to define an IArithmetic interface with Add, Multiply etc., and you'll be boxing and unboxing all over the place which can be a big performance impact.

    Your code is far worse than that; because you seem to be lacking a common interface for T you need to cast to object to make the generic cast succeed.

    Also if you don't have a common interface code similar to if (typeof(T) == typeof(Complex)) ... starts to appear, which is a big red flag when using generics; a generic class / method should work on an infinite number of types, not just a few preset types, that is what generic means.

I think you should take a step back and rethink your approach. When the language type system seems to be fighting against you and not helping in any way, its a sure sign you are doing something wrong.

Why don't you simply implement a non generic matrix of the largest type? A matrix of complex numbers. What is the benefit of having a matrix of floats or doubles? It can't be performance or memory efficiency because any non generic solution will be better than your current approach with all the boxing an unboxing going on.

UPDATE: After looking through the library you are basing this on, I'm not sure why you are not using Matrix<T> as intended in the first place: as a base type.

public class DoubleMatrix : Matrix<double>
{
    //now this is legal
    public static implicit operator DoubleMatrix(FloatMatrix matrix)
        => matrix.ToDouble();
}

Where FloatMatrix is obviously FloatMatrix: Matrix<Float>.

like image 21
InBetween Avatar answered Nov 15 '22 04:11

InBetween