{"action":"created","comment":{"url":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/comments/187340971","pull_request_review_id":119100480,"id":187340971,"diff_hunk":"@@ -396,123 +322,322 @@ class BasicLinOp : public LinOp, public ConvertibleTo<ConcreteLinOp> {\n  * // Suppose A is a matrix, b a rhs vector, and x an initial guess\n  * // Create a CG which runs for at most 1000 iterations, and stops after\n  * // reducing the residual norm by 6 orders of magnitude\n- * auto cg_factory = solver::CgFactory<>::create(gpu, 1000, 1e-6);\n+ * auto cg_factory = solver::Cg::Factory<>::create()\n+ *     .with_max_iters(1000)\n+ *     .with_rel_residual_goal(1e-6)\n+ *     .on_executor(gpu);\n  * // create a linear operator which represents the solver\n  * auto cg = cg_factory->generate(A);\n  * // solve the system\n- * cg->apply(b.get(), x.get());\n+ * cg->apply(gko::lend(b), gko::lend(x));\n  * ```\n  */\n-class LinOpFactory {\n+using LinOpFactory = AbstractFactory<LinOp, std::shared_ptr<const LinOp>>;\n+\n+\n+/**\n+ * Linear operators which support transposition should implement the\n+ * Transposable interface.\n+ *\n+ * It provides two functionalities, the normal transpose and the\n+ * conjugate transpose.\n+ *\n+ * The normal transpose returns the transpose of the linear operator without\n+ * changing any of its elements representing the operation, \\f$B = A^{T}\\f$.\n+ *\n+ * The conjugate transpose returns the conjugate of each of the elements and\n+ * additionally transposes the linear operator representing the operation, \\f$B\n+ * = A^{H}\\f$.\n+ *\n+ * Example: Transposing a Csr matrix:\n+ * ------------------------------------\n+ *\n+ * ```c++\n+ * //Transposing an object of LinOp type.\n+ * //The object you want to transpose.\n+ * auto op = matrix::Csr::create(exec);\n+ * //Transpose the object by first converting it to a transposable type.\n+ * auto trans = op->transpose();\n+ * ```\n+ */\n+class Transposable {\n public:\n+    virtual ~Transposable() = default;\n+\n     /**\n-     * Generates a Linear operator from the base linear operator.\n+     * Returns a LinOp representing the transpose of the Transposable object.\n      *\n-     * @param base  The base linear operator.\n+     * @return a pointer to the new transposed object\n      */\n-    virtual std::unique_ptr<LinOp> generate(\n-        std::shared_ptr<const LinOp> base) const = 0;\n+    virtual std::unique_ptr<LinOp> transpose() const = 0;\n+\n     /**\n-     * Gets the exector on which the Linear operator exists.\n+     * Returns a LinOp representing the conjugate transpose of the Transposable\n+     * object.\n      *\n-     * @return exec_ The executor.\n+     * @return a pointer to the new conjugate transposed object\n      */\n-    std::shared_ptr<const Executor> get_executor() const noexcept\n-    {\n-        return exec_;\n-    }\n-\n-protected:\n-    explicit LinOpFactory(std::shared_ptr<const Executor> exec)\n-        : exec_(std::move(exec))\n-    {}\n-\n-private:\n-    std::shared_ptr<const Executor> exec_;\n+    virtual std::unique_ptr<LinOp> conj_transpose() const = 0;\n };\n \n \n /**\n- * PreconditionedMethod is inherited by linear operators that support\n- * preconditioning.\n- *\n- * The class adds utilities for setting and getting the preconditioner operator.\n+ * A LinOp implementing this interface can read its data from a matrix_data\n+ * structure.\n  */\n-class PreconditionedMethod {\n+template <typename ValueType, typename IndexType>\n+class ReadableFromMatrixData {\n public:\n-    /**\n-     * Sets the preconditioner operator used by PreconditionedMethod.\n-     *\n-     * @param preconditioner  the precondtioner operator\n-     */\n-    void set_preconditioner(\n-        std::shared_ptr<const LinOp> preconditioner) noexcept\n-    {\n-        preconditioner_ = preconditioner;\n-    }\n+    virtual ~ReadableFromMatrixData() = default;\n \n     /**\n-     * Returns the preconditioner operator used by PreconditionedMethod.\n+     * Reads a matrix from a matrix_data structure.\n      *\n-     * @return the preconditioner operator\n+     * @param data  the matrix_data structure\n      */\n-    std::shared_ptr<const LinOp> get_preconditioner() const noexcept\n-    {\n-        return preconditioner_;\n-    }\n+    virtual void read(const matrix_data<ValueType, IndexType> &data) = 0;\n+};\n \n-protected:\n-    PreconditionedMethod() = default;\n \n-    explicit PreconditionedMethod(std::shared_ptr<const LinOp> preconditioner)\n-        : preconditioner_(preconditioner)\n-    {}\n+/**\n+ * A LinOp implementing this interface can write its data to a matrix_data\n+ * structure.\n+ */\n+template <typename ValueType, typename IndexType>\n+class WritableToMatrixData {\n+public:\n+    virtual ~WritableToMatrixData() = default;\n \n-    std::shared_ptr<const LinOp> preconditioner_{};\n+    /**\n+     * Writes a matrix to a matrix_data structure.\n+     *\n+     * @param data  the matrix_data structure\n+     */\n+    virtual void write(matrix_data<ValueType, IndexType> &data) const = 0;\n };\n \n \n /**\n- * PreconditionedMethodFactory is inherited by operator factories which\n- * generate PreconditionedMethod operators.\n+ * The EnableLinOp mixin can be used to provide sensible default implementations\n+ * of the majority of the LinOp and PolymorphicObject interface.\n+ *\n+ * The goal of the mixin is to facilitate the development of new LinOp, by\n+ * enabling the implementers to focus on the importan parts of their operator,\n+ * while the library takes care of generating the trivial utility functions.\n+ * The mixin will provide default implementations for the entire\n+ * PolymorphicObject interface, including a default implementation of\n+ * `copy_from` between objects of the new LinOp type. It will also hide the\n+ * default LinOp::apply() methods with versions that preserve the static type of\n+ * the object.\n+ *\n+ * Implementers of new LinOps are required to specify only the following\n+ * aspects:\n+ *\n+ * 1.  Creation of the LinOp: This can be facilitated via either\n+ *     EnableCreateMethod mixin (used mostly for matrix formats),\n+ *     or GKO_ENABLE_LIN_OP_FACTORY macro (used for operators created from other\n+ *     operators, like preconditioners and solvers).\n+ * 2.  Appllication of the LinOp: Implementers have to override the two\n+ *     overloads of the LinOp::apply_impl() virtual methods.\n  *\n- * The class adds utilities for setting and getting the LinOpFactory\n- * used to generate the preconditioner.\n+ * @tparam ConcreteLinOp  the concrete LinOp which is being implemented\n+ *                        [CRTP parameter]\n+ * @tparam PolymorphicBase  parent of ConcreteLinOp in the polymorphic\n+ *                          hierarchy, has to be a subclass of LinOp\n  */\n-class PreconditionedMethodFactory {\n+template <typename ConcreteLinOp, typename PolymorphicBase = LinOp>\n+class EnableLinOp\n+    : public EnablePolymorphicObject<ConcreteLinOp, PolymorphicBase>,\n+      public EnablePolymorphicAssignment<ConcreteLinOp> {\n public:\n-    /**\n-     * Sets the LinOpFactory which will be used by the\n-     * PreconditionedMethodFactory to generate the preconditioner.\n-     *\n-     * @param precond_factory  LinOp factory used to generate the preconditioner\n-     */\n-    void set_preconditioner(std::shared_ptr<const LinOpFactory> precond_factory)\n+    using EnablePolymorphicObject<ConcreteLinOp,\n+                                  PolymorphicBase>::EnablePolymorphicObject;\n+\n+    const ConcreteLinOp *apply(const LinOp *b, LinOp *x) const\n     {\n-        precond_factory_ = precond_factory;\n+        this->validate_application_parameters(b, x);\n+        auto exec = this->get_executor();\n+        this->apply_impl(make_temporary_clone(exec, b).get(),\n+                         make_temporary_clone(exec, x).get());\n+        return self();\n     }\n \n-    /**\n-     * Returns the LinOpFactory used by the PreconditionedMethodFactory to\n-     * generate the preconditioner.\n-     *\n-     * @return the LinOpFactory used to generate the preconditioner\n-     */\n-    std::shared_ptr<const LinOpFactory> get_preconditioner() const\n+    ConcreteLinOp *apply(const LinOp *b, LinOp *x)\n     {\n-        return precond_factory_;\n+        this->validate_application_parameters(b, x);\n+        auto exec = this->get_executor();\n+        this->apply_impl(make_temporary_clone(exec, b).get(),\n+                         make_temporary_clone(exec, x).get());\n+        return self();\n     }\n \n-protected:\n-    explicit PreconditionedMethodFactory(\n-        std::shared_ptr<const LinOpFactory> precond_factory_)\n-        : precond_factory_(precond_factory_)\n-    {}\n+    const ConcreteLinOp *apply(const LinOp *alpha, const LinOp *b,\n+                               const LinOp *beta, LinOp *x) const\n+    {\n+        this->validate_application_parameters(alpha, b, beta, x);\n+        auto exec = this->get_executor();\n+        this->apply_impl(make_temporary_clone(exec, alpha).get(),\n+                         make_temporary_clone(exec, b).get(),\n+                         make_temporary_clone(exec, beta).get(),\n+                         make_temporary_clone(exec, x).get());\n+        return self();\n+    }\n \n-    std::shared_ptr<const LinOpFactory> precond_factory_{};\n+    ConcreteLinOp *apply(const LinOp *alpha, const LinOp *b, const LinOp *beta,\n+                         LinOp *x)\n+    {\n+        this->validate_application_parameters(alpha, b, beta, x);\n+        auto exec = this->get_executor();\n+        this->apply_impl(make_temporary_clone(exec, alpha).get(),\n+                         make_temporary_clone(exec, b).get(),\n+                         make_temporary_clone(exec, beta).get(),\n+                         make_temporary_clone(exec, x).get());\n+        return self();\n+    }\n+\n+protected:\n+    GKO_ENABLE_SELF(ConcreteLinOp);\n };\n \n \n+/**\n+ * This is an alias for the EnableDefaultFactory mixin, which correctly sets the\n+ * template parameters to enable a subclass of LinOpFactory.\n+ *\n+ * @tparam ConcreteFactory  the concrete factory which is being implemented\n+ *                          [CRTP parmeter]\n+ * @tparam ConcreteLinOp  the concrete LinOp type which this factory produces,\n+ *                        needs to have a constructor which takes a\n+ *                        const ConcreteFactory *, and an\n+ *                        std::shared_ptr<const LiOp> as parameters.\n+ * @tparam ParametersType  a subclass of enable_parameters_type template which\n+ *                         defines all of the parameters of the factory\n+ * @tparam PolymorphicBase  parent of ConcreteFactory in the polymorphic\n+ *                          hierarchy, has to be a subclass of LinOpFactory\n+ */\n+template <typename ConcreteFactory, typename ConcreteLinOp,\n+          typename ParametersType, typename PolymorphicBase = LinOpFactory>\n+using EnableDefaultLinOpFactory =\n+    EnableDefaultFactory<ConcreteFactory, ConcreteLinOp, ParametersType,\n+                         PolymorphicBase>;\n+\n+\n+/**\n+ * This macro will generate a default implementation of a LinOpFactory for the\n+ * LinOp subclass it is defined in.\n+ *\n+ * The list of parameters for the factory should be defined in a code block\n+ * after the macro definition, and should contain a list of\n+ * GKO_FACTORY_PARMETER declarations. The class should provide a constructor\n+ * with signature\n+ * _lin_op(const _factory_name *, std::shared_ptr<const LinOp>)\n+ * which the factory will use a callback to construct the object.\n+ *\n+ * A minimal example of a linear operator is the following:\n+ *\n+ * ```c++\n+ * struct MyLinOp : public EnableLinOp<MyLinOp> {\n+ *     GKO_ENABLE_LIN_OP_FACTORY(MyLinOp, my_parameters, Factory) {\n+ *         // a factory parameter named \"my_value\", of type int and default\n+ *         // value of 5\n+ *         int GKO_FACTORY_PARAMETER(my_value, 5);\n+ *     };\n+ *     // constructor needed by EnableLinOp\n+ *     explicit MyLinOp(std::shared_ptr<const Executor> exec) {\n+ *         : EnableLinOp<MyLinOp>(exec) {}\n+ *     // constructor needed by the factory\n+ *     explicit MyLinOp(const Factory *factory,\n+ *                      std::shared_ptr<const LinOp> matrix)\n+ *         : EnableLinOp<MyLinOp>(factory->get_executor()), matrix->get_size()),\n+ *           // store factory's parameters locally\n+ *           my_parameters_{factory->get_parameters()},\n+ *     {\n+ *          int value = my_parameters_.my_value;\n+ *          // do something with value\n+ *     }\n+ * ```\n+ *\n+ * MyLinOp can then be created as follows:\n+ *\n+ * ```c++\n+ * auto exec = gko::ReferenceExecutor::create();\n+ * // create a factory with default `my_value` parameter\n+ * auto fact = MyLinOp::Factory::create().on_executor(exec);\n+ * // create a operator using the factory:\n+ * auto my_op = fact->generate(gko::matrix::Identity::create(exec, 2));\n+ * std::cout << my_op->get_my_parameters().my_value;  // prints 5\n+ *\n+ * // create a factory with custom `my_value` parameter\n+ * auto fact = MyLinOp::Factory::create().with_my_value(0).on_executor(exec);\n+ * // create a operator using the factory:\n+ * auto my_op = fact->generate(gko::matrix::Identity::create(exec, 2));\n+ * std::cout << my_op->get_my_parameters().my_value;  // prints 0\n+ * ```\n+ *\n+ * @note This macro only works with class __templates__ (not with regular\n+ *       classes). See <https://stackoverflow.com/q/50202718/9385966> for more\n+ *       details.\n+ *\n+ * @param _lin_op  concrete operator for which the factory is to be created\n+ *                 [CRTP parameter]\n+ * @param _parameters_name  name of the parameters member in the class\n+ *                          (its type is `<_parameters_name>_type`, the\n+ *                          protected member's name is `<_parameters_name>_`,\n+ *                          and the public getter's name is\n+ *                          `get_<_parameters_name>()`)\n+ * @param _facory_name  name of the generated factory type","path":"core/base/lin_op.hpp","position":736,"original_position":736,"commit_id":"0fe1b657db0fbe196d54d8a2e33b60f74f288590","original_commit_id":"0fe1b657db0fbe196d54d8a2e33b60f74f288590","user":{"login":"pratikvn","id":10301328,"avatar_url":"https://avatars1.githubusercontent.com/u/10301328?v=4","gravatar_id":"","url":"https://api.github.com/users/pratikvn","html_url":"https://github.com/pratikvn","followers_url":"https://api.github.com/users/pratikvn/followers","following_url":"https://api.github.com/users/pratikvn/following{/other_user}","gists_url":"https://api.github.com/users/pratikvn/gists{/gist_id}","starred_url":"https://api.github.com/users/pratikvn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pratikvn/subscriptions","organizations_url":"https://api.github.com/users/pratikvn/orgs","repos_url":"https://api.github.com/users/pratikvn/repos","events_url":"https://api.github.com/users/pratikvn/events{/privacy}","received_events_url":"https://api.github.com/users/pratikvn/received_events","type":"User","site_admin":false},"body":"nit: /s/_facory_name/_factory_name","created_at":"2018-05-10T14:09:29Z","updated_at":"2018-05-10T14:09:29Z","html_url":"https://github.com/ginkgo-project/ginkgo/pull/46#discussion_r187340971","pull_request_url":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/46","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/comments/187340971"},"html":{"href":"https://github.com/ginkgo-project/ginkgo/pull/46#discussion_r187340971"},"pull_request":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/46"}}},"pull_request":{"url":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/46","id":185719092,"html_url":"https://github.com/ginkgo-project/ginkgo/pull/46","diff_url":"https://github.com/ginkgo-project/ginkgo/pull/46.diff","patch_url":"https://github.com/ginkgo-project/ginkgo/pull/46.patch","issue_url":"https://api.github.com/repos/ginkgo-project/ginkgo/issues/46","number":46,"state":"open","locked":false,"title":"Class hierarchy improvements","user":{"login":"gflegar","id":1261711,"avatar_url":"https://avatars1.githubusercontent.com/u/1261711?v=4","gravatar_id":"","url":"https://api.github.com/users/gflegar","html_url":"https://github.com/gflegar","followers_url":"https://api.github.com/users/gflegar/followers","following_url":"https://api.github.com/users/gflegar/following{/other_user}","gists_url":"https://api.github.com/users/gflegar/gists{/gist_id}","starred_url":"https://api.github.com/users/gflegar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gflegar/subscriptions","organizations_url":"https://api.github.com/users/gflegar/orgs","repos_url":"https://api.github.com/users/gflegar/repos","events_url":"https://api.github.com/users/gflegar/events{/privacy}","received_events_url":"https://api.github.com/users/gflegar/received_events","type":"User","site_admin":false},"body":"## Summary\r\n\r\nThis PR is a major refactoring of Ginkgo's base classes, in preparation for support of stopping criteria and loggers. It makes the interfaces of all these classes more uniform by adding a `PolymorphicObject` base class which provides a uniform interface for simple operations useful for all such objects (linear operators, operator factories, and future loggers and criteria). There are also additional improvements on factories and linear operators, each described in their own section.\r\n\r\n## `PolymorphicObject`\r\n\r\nA `PolymorphicObject` is designed as an abstract base class for all polymorphic objects. It is executor-aware (i.e. all polymorphic object \"belong\" to a certain executor), and exposes virtual method for cloning and copying polymorphic objects.\r\n\r\nIt is easy to notice that for a specific (concrete) implementation of a polymorphic object, all these methods are trivially implemented using a constructor which takes an executor, and the assignment operator. For this reason, to simplify the implementation of polymorphic objects and promote code reuse, this PR provides the `EnablePolymorphicObject` and `EnableAbstractPolymorphicObject` [mixins](https://en.wikipedia.org/wiki/Mixin) that provide default implementations of these methods.\r\n\r\nThe `PolymorphicObject` class is implemented in a way that reduces the amount of conversions needed when using it. As an example, consider the `clone()` method which will create an exact copy of a polymorphic object. To make it a part of the PolymorphicObject interface it needs to have the following signature:\r\n\r\n```c++\r\nvirtual std::unique_ptr<PolymorphicObject> clone() const = 0;\r\n```\r\n\r\nHowever, when implementing the method for a concrete polymorphic object, say a CSR matrix, we do know more about the return type - it should be a unique pointer to a CSR matrix, so we would _like_ to write something like this:\r\n\r\n```c++\r\nstd::unique_ptr<Csr> clone() const override { /* implementation of clone */ }\r\n```\r\n\r\nUnfortunately, changing the return type of a virtual method is only allowed if the types are [covariant](http://en.cppreference.com/w/cpp/language/virtual#Covariant_return_types), which is not the case here.\r\nHowever, we are still able to support this by implementing clone() using a helper method:\r\n\r\n```c++\r\nclass PolymorphicObject {\r\npublic:\r\n    // the clone method is no longer virtual\r\n    std::unique_ptr<PolymorphicObject> clone() const { return this->clone_impl(); }\r\nprotected:\r\n    // there is a new helper method which is virtual\r\n    virtual std::unique_ptr<PolymorphicObject> clone_impl() const = 0;\r\n};\r\n```\r\n\r\nThen, in the concrete polymorphic object, we __hide__ the `clone()` method, and __override__ the `clone_impl()` method:\r\n\r\n```c++\r\nclass Csr {\r\npublic:\r\n    // since this method is no longer virtual, we can change its return type\r\n    // however, we need to cast the result of clone_impl()\r\n    std::unique_ptr<Csr> clone() const { \r\n        return std::unique_ptr<Csr>(this->clone_impl().release());\r\n    }\r\nprotected:\r\n    // we implement clone for Csr, keeping it's signature\r\n    std::unique_ptr<PolymorphicObject> clone_impl() const override { /* implementation of clone */ }\r\n};\r\n```\r\n\r\nBoth of these things are done automatically for clone and other methods when using the `EnablePolymorphicObject` mixin. For abstract classes that inherit from PolymorphicObject, (e.g. `LinOp`) we still want to hide the original `clone()` method, so it returns an instance of `LinOp` when called on a `LinOp`. For this reason, there is a `EnableAbstractPolymorphicObject` mixin that will only hide the public methods, but leave the implementation methods unimplemented.\r\n\r\nThere are three conventions here that are used throughout the code:\r\n\r\n_Convention 1:_ All mixin classes start with `Enable` or `enable_`.  \r\n_Convention 2:_ All virtual implementation methods end with `_impl`.  \r\n_Convention 3:_ All mixins need a reference to the class that implements them (know also as the CRTP pattern in C++). Such template parmeters have been marked with `[CRTP parameter]` in the documentation.\r\n\r\nNotice that _Convention 1_ seems to break the rule that class names should be nouns. This is intentional, since mixin \"classes\" should never be used as classes themselves, but just \"included\" (via inheritance) into other classes to provide specific functionality (unfortunately, C++ does't have built-in support for mixins, so misusing inheritence for this purpose is the only way of implementing them). The word \"enable\" was chosen to be compatible with the C++ standard library (see [`std::enable_shared_from_this`](http://en.cppreference.com/w/cpp/memory/enable_shared_from_this) for an example of a mixin in the standard library).\r\n\r\n## `LinOp`\r\n\r\nThanks to `PolymorphicObject`, the `LinOp` interface has been simplified. It now inherits all of its memory management methods from it, and just adds the `apply()` methods. The `apply()` methods have also been enhanced by adding a [fluent interface](https://en.wikipedia.org/wiki/Fluent_interface), automatically verifying the sizes of input parameters (so the implementers don't have to do it anymore), and automatically copying the parameters to the correct executor if they were not already there.\r\n\r\nIn order to enable this, the same \"trick\" of having a public non-virtual `apply()` method (which is hidden in subclases), and a protected virtual `apply_impl()` method was used here.\r\nThe public method validates the sizes, and copies the parameters, and then passes the control to the `apply_impl()` method which the user has to override with their own implementation.\r\n\r\nThe `BasicLinOp` mixin has been updated accordingly, and renamed to `EnableLinOp`, to be consistent with _Convention 1_.\r\n\r\nFinally, the `num_stored_nonzeros` property has been removed (closes #47), and the `num_rows` and `num_cols` properties have been merged into a single property `size`, of the newly created type `dim`, which has `num_rows` and `num_cols` properties, and provides some useful methods for size manipulation (more can be added if needed later). I find this easier to use, as most of the time we manipulate both dimensions together (e.g. to get transposed dimensions, it is now possible to write `transpose(size)`). All existing linear operators have been updated to use this new syntax accordingly.\r\n\r\n## `AbstractFactory` and `LinOpFactory`\r\n\r\nThe `LinOpFactory` interface (which had a generate method capable of creating `LinOp`s from other `LinOp`s) has been generalized into the `AbstractFactory` interface template which creates `AbstractProductType`s from `ComponentsType`s. There is still a symbol named `LinOpFactory`, but this has now become only an alias for a specific kind of `AbstractFactory`.\r\nThe idea is that the `AbstractFactory` interface can be reused to create stopping criteria and logger factories if needed.\r\n\r\nAnother improvement to `AbstractFactory` is that it has become a first-class citizen together with `LinOp`, as it now supports standard management operations such as copying and cloning (it inherits from `PolymorphicObject`.\r\n\r\nAs most factories we had were mostly boilerplate code (set parameters, pass them to the constructor of the object being built), we now had an `EnableDefaultFactory` mixin that is able to generate a complete factory implementation, give the adequate description. For `LinOpFactory`, there is also a `GKO_ENABLE_LIN_OP_FACTORY` macro that provides a simpler interface to `EnableDefaultFactory`.\r\nThe default factory uses a fluent interface to fill the parameters of the factory, and accepts default parameters, so the users do not have to specify all of them.\r\nAll classes that use factories (except for BlockJacobi - it needs significant refactoring anyway, so I skipped it for now) have been updated to use this. See one of the solvers and the unit tests for examples of enabling a default factory for a linear operator, and using such a factory. \r\n\r\n## Other utilities\r\n\r\nThere are a couple of minor tweaks that were added as support for the major changes, but can be useful on their own:\r\n\r\n1.  The most significant one is the `temporary_clone` \"smart pointer\". This utility will make sure that the object is on the specified executor, by copying it only if it's not already there. For non-constant objects, it will also copy the data back to the original location before deallocating it. (This is useful in the new implementation of `LinOp::apply()`, and anywhere where ensuring the correct executor of an object is required.)\r\n2. As already mentioned, the `dim` class connects both the `num_rows` and `num_cols` properties into a single property, and adds some utilities for manipulating them. The utility `size` class used in some assertions has been completely replaced by `dim`.\r\n\r\n## Changes to the code\r\n\r\nAs you probably noticed, the is a significant number of lines changed by this PR. However, most of them are trivial search/replace changes resulting from a changed interface. To summarize them in one place these are:\r\n\r\n1.   `get_num_rows()` and `get_num_cols()` calls have been replaced by `get_size().num_rows` and `get_size().num_cols`.\r\n2.  constructors that take `num_rows` and `num_cols` arguments have been modified to instead take a `size` argument. This also made it possible to remove some of the constructors by providing default parameters in some of the other ones. Calls to those constructors have also been updated.\r\n3.  The `get_num_stored_elements()` method has been added to concrete operators where it makes sense, and the assertions using this method have been removed for classes where the method doesn't makes sense.\r\n4.  Solver factories have been removed and replaced with automatically generated ones. Since these factories have a slightly different (fluent) interface, the calls to these factories have been updated.\r\n5.  Public `apply()` overrides in concrete operators have been replaced with protected `apply_impl()` overrides.\r\n\r\n## References\r\n\r\nThe following is a list of references which explain some of the concepts used in this PR in more detail:\r\n\r\n1.  [Method overriding vs method hiding](https://stackoverflow.com/a/19737267/9385966)\r\n2.  [Mixins](https://en.wikipedia.org/wiki/Mixin)\r\n3.  [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)\r\n4.  [Fluent interface](https://en.wikipedia.org/wiki/Fluent_interface)\r\n5.  [Abstract factory pattern](https://en.wikipedia.org/wiki/Abstract_factory_pattern)","created_at":"2018-05-03T13:37:47Z","updated_at":"2018-05-10T14:09:29Z","closed_at":null,"merged_at":null,"merge_commit_sha":"13c67c54cd953c1fa10e5009ec310e2909e2d018","assignee":{"login":"gflegar","id":1261711,"avatar_url":"https://avatars1.githubusercontent.com/u/1261711?v=4","gravatar_id":"","url":"https://api.github.com/users/gflegar","html_url":"https://github.com/gflegar","followers_url":"https://api.github.com/users/gflegar/followers","following_url":"https://api.github.com/users/gflegar/following{/other_user}","gists_url":"https://api.github.com/users/gflegar/gists{/gist_id}","starred_url":"https://api.github.com/users/gflegar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gflegar/subscriptions","organizations_url":"https://api.github.com/users/gflegar/orgs","repos_url":"https://api.github.com/users/gflegar/repos","events_url":"https://api.github.com/users/gflegar/events{/privacy}","received_events_url":"https://api.github.com/users/gflegar/received_events","type":"User","site_admin":false},"assignees":[{"login":"gflegar","id":1261711,"avatar_url":"https://avatars1.githubusercontent.com/u/1261711?v=4","gravatar_id":"","url":"https://api.github.com/users/gflegar","html_url":"https://github.com/gflegar","followers_url":"https://api.github.com/users/gflegar/followers","following_url":"https://api.github.com/users/gflegar/following{/other_user}","gists_url":"https://api.github.com/users/gflegar/gists{/gist_id}","starred_url":"https://api.github.com/users/gflegar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gflegar/subscriptions","organizations_url":"https://api.github.com/users/gflegar/orgs","repos_url":"https://api.github.com/users/gflegar/repos","events_url":"https://api.github.com/users/gflegar/events{/privacy}","received_events_url":"https://api.github.com/users/gflegar/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":801859198,"url":"https://api.github.com/repos/ginkgo-project/ginkgo/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":849442845,"url":"https://api.github.com/repos/ginkgo-project/ginkgo/labels/proposal","name":"proposal","color":"cce579","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/46/commits","review_comments_url":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/46/comments","review_comment_url":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ginkgo-project/ginkgo/issues/46/comments","statuses_url":"https://api.github.com/repos/ginkgo-project/ginkgo/statuses/0fe1b657db0fbe196d54d8a2e33b60f74f288590","head":{"label":"ginkgo-project:class_hierarchy_improvements","ref":"class_hierarchy_improvements","sha":"0fe1b657db0fbe196d54d8a2e33b60f74f288590","user":{"login":"ginkgo-project","id":35341738,"avatar_url":"https://avatars2.githubusercontent.com/u/35341738?v=4","gravatar_id":"","url":"https://api.github.com/users/ginkgo-project","html_url":"https://github.com/ginkgo-project","followers_url":"https://api.github.com/users/ginkgo-project/followers","following_url":"https://api.github.com/users/ginkgo-project/following{/other_user}","gists_url":"https://api.github.com/users/ginkgo-project/gists{/gist_id}","starred_url":"https://api.github.com/users/ginkgo-project/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ginkgo-project/subscriptions","organizations_url":"https://api.github.com/users/ginkgo-project/orgs","repos_url":"https://api.github.com/users/ginkgo-project/repos","events_url":"https://api.github.com/users/ginkgo-project/events{/privacy}","received_events_url":"https://api.github.com/users/ginkgo-project/received_events","type":"Organization","site_admin":false},"repo":{"id":117122510,"name":"ginkgo","full_name":"ginkgo-project/ginkgo","owner":{"login":"ginkgo-project","id":35341738,"avatar_url":"https://avatars2.githubusercontent.com/u/35341738?v=4","gravatar_id":"","url":"https://api.github.com/users/ginkgo-project","html_url":"https://github.com/ginkgo-project","followers_url":"https://api.github.com/users/ginkgo-project/followers","following_url":"https://api.github.com/users/ginkgo-project/following{/other_user}","gists_url":"https://api.github.com/users/ginkgo-project/gists{/gist_id}","starred_url":"https://api.github.com/users/ginkgo-project/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ginkgo-project/subscriptions","organizations_url":"https://api.github.com/users/ginkgo-project/orgs","repos_url":"https://api.github.com/users/ginkgo-project/repos","events_url":"https://api.github.com/users/ginkgo-project/events{/privacy}","received_events_url":"https://api.github.com/users/ginkgo-project/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ginkgo-project/ginkgo","description":"Numerical linear algebra software package","fork":false,"url":"https://api.github.com/repos/ginkgo-project/ginkgo","forks_url":"https://api.github.com/repos/ginkgo-project/ginkgo/forks","keys_url":"https://api.github.com/repos/ginkgo-project/ginkgo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ginkgo-project/ginkgo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ginkgo-project/ginkgo/teams","hooks_url":"https://api.github.com/repos/ginkgo-project/ginkgo/hooks","issue_events_url":"https://api.github.com/repos/ginkgo-project/ginkgo/issues/events{/number}","events_url":"https://api.github.com/repos/ginkgo-project/ginkgo/events","assignees_url":"https://api.github.com/repos/ginkgo-project/ginkgo/assignees{/user}","branches_url":"https://api.github.com/repos/ginkgo-project/ginkgo/branches{/branch}","tags_url":"https://api.github.com/repos/ginkgo-project/ginkgo/tags","blobs_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/refs{/sha}","trees_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ginkgo-project/ginkgo/statuses/{sha}","languages_url":"https://api.github.com/repos/ginkgo-project/ginkgo/languages","stargazers_url":"https://api.github.com/repos/ginkgo-project/ginkgo/stargazers","contributors_url":"https://api.github.com/repos/ginkgo-project/ginkgo/contributors","subscribers_url":"https://api.github.com/repos/ginkgo-project/ginkgo/subscribers","subscription_url":"https://api.github.com/repos/ginkgo-project/ginkgo/subscription","commits_url":"https://api.github.com/repos/ginkgo-project/ginkgo/commits{/sha}","git_commits_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/commits{/sha}","comments_url":"https://api.github.com/repos/ginkgo-project/ginkgo/comments{/number}","issue_comment_url":"https://api.github.com/repos/ginkgo-project/ginkgo/issues/comments{/number}","contents_url":"https://api.github.com/repos/ginkgo-project/ginkgo/contents/{+path}","compare_url":"https://api.github.com/repos/ginkgo-project/ginkgo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ginkgo-project/ginkgo/merges","archive_url":"https://api.github.com/repos/ginkgo-project/ginkgo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ginkgo-project/ginkgo/downloads","issues_url":"https://api.github.com/repos/ginkgo-project/ginkgo/issues{/number}","pulls_url":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls{/number}","milestones_url":"https://api.github.com/repos/ginkgo-project/ginkgo/milestones{/number}","notifications_url":"https://api.github.com/repos/ginkgo-project/ginkgo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ginkgo-project/ginkgo/labels{/name}","releases_url":"https://api.github.com/repos/ginkgo-project/ginkgo/releases{/id}","deployments_url":"https://api.github.com/repos/ginkgo-project/ginkgo/deployments","created_at":"2018-01-11T16:14:21Z","updated_at":"2018-05-07T06:03:24Z","pushed_at":"2018-05-10T13:03:30Z","git_url":"git://github.com/ginkgo-project/ginkgo.git","ssh_url":"git@github.com:ginkgo-project/ginkgo.git","clone_url":"https://github.com/ginkgo-project/ginkgo.git","svn_url":"https://github.com/ginkgo-project/ginkgo","homepage":null,"size":1511,"stargazers_count":7,"watchers_count":7,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"archived":false,"open_issues_count":17,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":2,"open_issues":17,"watchers":7,"default_branch":"develop"}},"base":{"label":"ginkgo-project:develop","ref":"develop","sha":"557954c9c6100524069eaef5013c107b08015bc0","user":{"login":"ginkgo-project","id":35341738,"avatar_url":"https://avatars2.githubusercontent.com/u/35341738?v=4","gravatar_id":"","url":"https://api.github.com/users/ginkgo-project","html_url":"https://github.com/ginkgo-project","followers_url":"https://api.github.com/users/ginkgo-project/followers","following_url":"https://api.github.com/users/ginkgo-project/following{/other_user}","gists_url":"https://api.github.com/users/ginkgo-project/gists{/gist_id}","starred_url":"https://api.github.com/users/ginkgo-project/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ginkgo-project/subscriptions","organizations_url":"https://api.github.com/users/ginkgo-project/orgs","repos_url":"https://api.github.com/users/ginkgo-project/repos","events_url":"https://api.github.com/users/ginkgo-project/events{/privacy}","received_events_url":"https://api.github.com/users/ginkgo-project/received_events","type":"Organization","site_admin":false},"repo":{"id":117122510,"name":"ginkgo","full_name":"ginkgo-project/ginkgo","owner":{"login":"ginkgo-project","id":35341738,"avatar_url":"https://avatars2.githubusercontent.com/u/35341738?v=4","gravatar_id":"","url":"https://api.github.com/users/ginkgo-project","html_url":"https://github.com/ginkgo-project","followers_url":"https://api.github.com/users/ginkgo-project/followers","following_url":"https://api.github.com/users/ginkgo-project/following{/other_user}","gists_url":"https://api.github.com/users/ginkgo-project/gists{/gist_id}","starred_url":"https://api.github.com/users/ginkgo-project/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ginkgo-project/subscriptions","organizations_url":"https://api.github.com/users/ginkgo-project/orgs","repos_url":"https://api.github.com/users/ginkgo-project/repos","events_url":"https://api.github.com/users/ginkgo-project/events{/privacy}","received_events_url":"https://api.github.com/users/ginkgo-project/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ginkgo-project/ginkgo","description":"Numerical linear algebra software package","fork":false,"url":"https://api.github.com/repos/ginkgo-project/ginkgo","forks_url":"https://api.github.com/repos/ginkgo-project/ginkgo/forks","keys_url":"https://api.github.com/repos/ginkgo-project/ginkgo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ginkgo-project/ginkgo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ginkgo-project/ginkgo/teams","hooks_url":"https://api.github.com/repos/ginkgo-project/ginkgo/hooks","issue_events_url":"https://api.github.com/repos/ginkgo-project/ginkgo/issues/events{/number}","events_url":"https://api.github.com/repos/ginkgo-project/ginkgo/events","assignees_url":"https://api.github.com/repos/ginkgo-project/ginkgo/assignees{/user}","branches_url":"https://api.github.com/repos/ginkgo-project/ginkgo/branches{/branch}","tags_url":"https://api.github.com/repos/ginkgo-project/ginkgo/tags","blobs_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/refs{/sha}","trees_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ginkgo-project/ginkgo/statuses/{sha}","languages_url":"https://api.github.com/repos/ginkgo-project/ginkgo/languages","stargazers_url":"https://api.github.com/repos/ginkgo-project/ginkgo/stargazers","contributors_url":"https://api.github.com/repos/ginkgo-project/ginkgo/contributors","subscribers_url":"https://api.github.com/repos/ginkgo-project/ginkgo/subscribers","subscription_url":"https://api.github.com/repos/ginkgo-project/ginkgo/subscription","commits_url":"https://api.github.com/repos/ginkgo-project/ginkgo/commits{/sha}","git_commits_url":"https://api.github.com/repos/ginkgo-project/ginkgo/git/commits{/sha}","comments_url":"https://api.github.com/repos/ginkgo-project/ginkgo/comments{/number}","issue_comment_url":"https://api.github.com/repos/ginkgo-project/ginkgo/issues/comments{/number}","contents_url":"https://api.github.com/repos/ginkgo-project/ginkgo/contents/{+path}","compare_url":"https://api.github.com/repos/ginkgo-project/ginkgo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ginkgo-project/ginkgo/merges","archive_url":"https://api.github.com/repos/ginkgo-project/ginkgo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ginkgo-project/ginkgo/downloads","issues_url":"https://api.github.com/repos/ginkgo-project/ginkgo/issues{/number}","pulls_url":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls{/number}","milestones_url":"https://api.github.com/repos/ginkgo-project/ginkgo/milestones{/number}","notifications_url":"https://api.github.com/repos/ginkgo-project/ginkgo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ginkgo-project/ginkgo/labels{/name}","releases_url":"https://api.github.com/repos/ginkgo-project/ginkgo/releases{/id}","deployments_url":"https://api.github.com/repos/ginkgo-project/ginkgo/deployments","created_at":"2018-01-11T16:14:21Z","updated_at":"2018-05-07T06:03:24Z","pushed_at":"2018-05-10T13:03:30Z","git_url":"git://github.com/ginkgo-project/ginkgo.git","ssh_url":"git@github.com:ginkgo-project/ginkgo.git","clone_url":"https://github.com/ginkgo-project/ginkgo.git","svn_url":"https://github.com/ginkgo-project/ginkgo","homepage":null,"size":1511,"stargazers_count":7,"watchers_count":7,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"archived":false,"open_issues_count":17,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":2,"open_issues":17,"watchers":7,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/46"},"html":{"href":"https://github.com/ginkgo-project/ginkgo/pull/46"},"issue":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/issues/46"},"comments":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/issues/46/comments"},"review_comments":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/46/comments"},"review_comment":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/pulls/46/commits"},"statuses":{"href":"https://api.github.com/repos/ginkgo-project/ginkgo/statuses/0fe1b657db0fbe196d54d8a2e33b60f74f288590"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"pratikvn"}}