Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datastructure for objects with prototype (clone) design pattern. #8702

Closed
hivert opened this issue Apr 17, 2010 · 13 comments
Closed

Datastructure for objects with prototype (clone) design pattern. #8702

hivert opened this issue Apr 17, 2010 · 13 comments

Comments

@hivert
Copy link

hivert commented Apr 17, 2010

The idea is inspired from the "prototype" design pattern (see [Pro], [GOF]).

I want to define subclasses of Element with the following behavior: Those
classes are intended to be used to model mathematical objects, which are by
essence immutable. However, in many occasions, one wants to construct the
data-structure encoding of a new mathematical object by small modifications of
the data structure encoding some already built object. This is a very common
stuff for example in matrices: For example: given a matrix M we want to
replace every non_zero position by 1

            res = copy(M)
            for pos in res._nonzero_positions_by_row():
                res[pos] = 1
            res.set_immutable()

However in many cases, for the resulting data-structure to correctly encode
the mathematical object, some structural invariants must hold (say for example
we want that the matrix is symmetric). One problem is that, in many cases,
during the modification process, there is no possibility but to break the
invariants. Here there is no way to keep the matrix symmetric during the
replacement by 1...

A typical example in combinatorics, in a list modeling a permutation of
{1,...,n}, the integers must be distinct. A very common operation is to
take a permutation to make a copy with some small modifications, like
exchanging two consecutive values in the list or cycling some values. Though
the result is clearly a permutations there's no way to avoid breaking the
permutations invariants at some point during the modifications.

So the idea is the following: to allows local breaking of invariant on a
locally mutable copy and to check that things are restored in a proper state
at the end. So I wrote a small class called ClonableElement and several
derived subclasses (clone is the standard name for the copy method in the
"prototype" design pattern):

A class C inheriting from ClonableElement must implement the following
two methods

    - obj.__copy__() -- returns a fresh copy of obj
    - obj.check() -- returns nothing, raise an exception if obj
      doesn't satisfies the data structure invariants

Then, given an instance obj of C, the following sequences of
instructions ensures that the invariants of new_obj are properly
restored at the end

       with obj.clone() as new_obj:
           ...
           # lot of invariant breaking modifications on new_obj
           ...
       # invariants are ensured to be fulfilled

The following equivalent sequence of instructions can be used if speed is
needed, in particular in Cython code (unfortunately, the handling of the with
instruction make some overhead)...

       new_obj = obj.__copy__()
       ...
       # lot of invariant breaking modifications on new_obj
       ...
       new_obj.set_immutable()
       new_obj.check()
       # invariants are ensured to be fulfilled

I also took to chance to handle hashing...

This is the future Cython replacement for CombinatorialObject.

[Pro] Prototype pattern http://en.wikipedia.org/wiki/Prototype_pattern

[GOF] Design Patterns: Elements of Reusable Object-Oriented
Software. E. Gamma; R. Helm; R. Johnson; J. Vlissides (1994).
Addison-Wesley. ISBN 0-201-63361-2.

CC: @novoselt @mwhansen @sagetrac-sage-combinat

Component: combinatorics

Author: Florent Hivert

Reviewer: Mike Hansen

Merged: sage-4.6.2.alpha0

Issue created by migration from https://trac.sagemath.org/ticket/8702

@hivert hivert self-assigned this Apr 17, 2010
@novoselt novoselt added this to the sage-4.4.4 milestone Jun 10, 2010
@hivert

This comment has been minimized.

@hivert
Copy link
Author

hivert commented Oct 24, 2010

comment:4

Hi mike,

I adressed your comment:

  1. Is there a good use case for allowing None to be passed in to
    ClonableArray, ClonableList, and ConableIntArray. There is a bit of
    mental overhead in always having to remember to check that self._list
    is always an actual list.

and updated a new patch... Please review.

@hivert
Copy link
Author

hivert commented Oct 24, 2010

Reviewer: Mike Hansen

@hivert
Copy link
Author

hivert commented Oct 24, 2010

comment:5

and updated a new patch... Please review.

Sorry: I should have said that I also folded your review patch... Thanks for it.

@mwhansen
Copy link
Contributor

mwhansen commented Nov 4, 2010

comment:6

Looks good to me.

@hivert
Copy link
Author

hivert commented Nov 4, 2010

comment:7

Replying to @mwhansen:

Looks good to me.

Thanks for the review !

@jdemeyer
Copy link

jdemeyer commented Nov 6, 2010

comment:8

I get doctest errors:

**********************************************************************
File "/mnt/usb1/scratch/jdemeyer/merger/sage-4.6.1.alpha1/devel/sage-main/sage/structure/list_clone_timings.py", line 8:
    sage: from sage.structure.list_clone_timmings import *
Exception raised:
    Traceback (most recent call last):
      File "/mnt/usb1/scratch/jdemeyer/merger/sage-4.6.1.alpha1/local/bin/ncadoctest.py", line 1231, in run_one_test
        self.run_one_example(test, example, filename, compileflags)
      File "/mnt/usb1/scratch/jdemeyer/merger/sage-4.6.1.alpha1/local/bin/sagedoctest.py", line 38, in run_one_example
        OrigDocTestRunner.run_one_example(self, test, example, filename, compileflags)
      File "/mnt/usb1/scratch/jdemeyer/merger/sage-4.6.1.alpha1/local/bin/ncadoctest.py", line 1172, in run_one_example
        compileflags, 1) in test.globs
      File "<doctest __main__.example_0[2]>", line 1, in <module>
        from sage.structure.list_clone_timmings import *###line 8:
    sage: from sage.structure.list_clone_timmings import *
    ImportError: No module named list_clone_timmings
**********************************************************************

@hivert
Copy link
Author

hivert commented Nov 19, 2010

Attachment: diff-8702.gz

@hivert
Copy link
Author

hivert commented Nov 19, 2010

comment:9

Replying to @jdemeyer:

I get doctest errors:

**********************************************************************
File "/mnt/usb1/scratch/jdemeyer/merger/sage-4.6.1.alpha1/devel/sage-main/sage/structure/list_clone_timings.py", line 8:
    sage: from sage.structure.list_clone_timmings import *
[...]

Oups ! I forgot to fold some corrective patches. I just resubmitted the corrected version. To ease the review I also uploaded the diff between the older version and the new one. Do not apply this chunk of code.

Only apply trac_8702-list_clone-fh.patch

@hivert
Copy link
Author

hivert commented Nov 19, 2010

Attachment: trac_8702-list_clone-fh.2.patch.gz

@hivert
Copy link
Author

hivert commented Nov 19, 2010

comment:10

Added a missing utf8 tag on the file list_clone_timings.py...

Apply only trac_8702-list_clone-fh.2.patch

@hivert
Copy link
Author

hivert commented Nov 19, 2010

comment:11

Attachment: trac_8702-list_clone-fh.patch.gz

Oops ! I forgot to add Copyright notices... Sorry for the mess.

Apply only trac_8702-list_clone-fh.patch

@jdemeyer
Copy link

Merged: sage-4.6.2.alpha0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants