ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:111302] [Ruby master Feature#8223] Make Matrix more omnivorous.
       [not found] <redmine.issue-8223.20130405205559.2@ruby-lang.org>
@ 2022-12-15  8:48 ` hsbt (Hiroshi SHIBATA)
  0 siblings, 0 replies; only message in thread
From: hsbt (Hiroshi SHIBATA) @ 2022-12-15  8:48 UTC (permalink / raw
  To: ruby-core

Issue #8223 has been updated by hsbt (Hiroshi SHIBATA).



Status changed from Open to Third Party's Issue



`matrix` has been extracted to https://github.com/ruby/matrix.



----------------------------------------

Feature #8223: Make Matrix more omnivorous.

https://bugs.ruby-lang.org/issues/8223#change-100669



* Author: Anonymous

* Status: Third Party's Issue

* Priority: Normal

* Assignee: marcandre (Marc-Andre Lafortune)

----------------------------------------

Let's imagine a class Metre, whose instances represent physical magnitudes in metres.



    class Metre

      attr_reader :magnitude

      def initialize magnitude; @magnitude = magnitude end

      def to_s; magnitude.to_s + ".m" end

    end



Let's say that metres can be multiplied by a number:



    class Metre

      def * multiplicand

        case multiplicand

        when Numeric then Metre.new( magnitude * multiplicand )

        else

          raise "Metres can only be multiplied by numbers, multiplication by #{multiplicand.class} attempted!"

        end

      end

    end



And that they can be summed up with other magnitudes in metres, but, as a feature,

not with numbers (apples, pears, seconds, kelvins...).



    class Metre

      def + summand

        case summand

        when Metre then Metre.new( magnitude + summand.magnitude )

        else

          raise "Metres can only be summed with metres, summation with #{summand.class} attempted!"

        end

      end

    end



Now with one more convenience constructor Numeric#m:



    class Numeric

      def m; Metre.new self end

    end



We can write expressions such as



    3.m + 5.m

    #=> 8.m

    3.m * 2

    #=> 6.m



And with defined #coerce:



    class Metre

      def coerce other; [ self, other ] end

    end



Also this expression is valid:



    2 * 3.m

    #=> 6.m



Before long, the user will want to make a matrix of magnitudes:



    require 'matrix'

    mx = Matrix.build 2, 2 do 1.m end

    #=> Matrix[[1.m, 1.m], [1.m, 1.m]]



It works, but the joy does not last long. The user will fail miserably if ze wants to perform matrix multiplication:



    cv = Matrix.column_vector [1, 1]

    mx * cv

    #=> RuntimeError: Metres can only be summed with metres, summation with Fixnum attempted!

    # where 2.m would be expected



In theory, everything should be O.K., since Metre class has both metre summation and multiplication by a number defined. The failure happens due to the internal workings of the Matrix class, which assumes that the elements can be summed together with numeric 0. But it is a feature of metres, that they are picky and allow themselves to be summed only with other Metre instances.



In my real physical units library that I have written, I have solved this problem by

defining an über zero object that produces the expected result, when summed with objects, that would otherwise not lend themselves to summation with ordinary numeric 0,

and patching the Matrix class so that it uses this über zero instead of the ordinary one.



But this is not a very systematic solution. Actually, I think that the Matrix class would be more flexible, if, instead of simply using 0, it asked the elements of the matrix what their zero is, as in:



    class << Metre

      def zero; new 0 end

    end



But of course, that would also require that ordinary numeric classes can tell what their zero is, as in:



    def Integer.zero; 0 end

    def Float.zero; 0.0 end

    def Complex.zero; Complex 0.0, 0.0 end

    # etc.



I think that this way of doing things (that is, having #zero methods in numeric classes and making Matrix actually require the class of the objects in it to have public class method #zero defined) would make everything more consistent and more algebra-like. I am having this problem for already almost half a year, but I only gathered courage today to encumber you guys with this proposal. Please don't judge me harshly for it. I have actually already seen something like this, in particular with bigdecimal's Jacobian (http://ruby-doc.org/stdlib-2.0/libdoc/bigdecimal/rdoc/Jacobian.html), which requires that the object from which the Jacobian is computed implements methods #zero, #one, #two etc. Sorry again.







-- 

https://bugs.ruby-lang.org/

 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-12-15  8:48 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <redmine.issue-8223.20130405205559.2@ruby-lang.org>
2022-12-15  8:48 ` [ruby-core:111302] [Ruby master Feature#8223] Make Matrix more omnivorous hsbt (Hiroshi SHIBATA)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).