15
15
:widths: 30, 70
16
16
:delim: |
17
17
18
- :meth:`~Bijectionist.set_intertwining_relations` | Declare that the statistic intertwines with other maps.
19
- :meth:`~Bijectionist.set_constant_blocks` | Declare that the statistic is constant on some sets.
20
18
:meth:`~Bijectionist.set_statistics` | Declare statistics that are preserved by the bijection.
21
19
:meth:`~Bijectionist.set_value_restrictions` | Restrict the values of the statistic on an element.
20
+ :meth:`~Bijectionist.set_constant_blocks` | Declare that the statistic is constant on some sets.
22
21
:meth:`~Bijectionist.set_distributions` | Restrict the distribution of values of the statistic on some elements.
22
+ :meth:`~Bijectionist.set_intertwining_relations` | Declare that the statistic intertwines with other maps.
23
+ :meth:`~Bijectionist.set_pseudo_inverse_relation` | Declare that the statistic satisfies a certain relation.
24
+ :meth:`~Bijectionist.set_homomesic` | Declare that the statistic is homomesic with respect to a given set partition.
25
+
23
26
24
27
:meth:`~Bijectionist.statistics_table` | Print a table collecting information on the given statistics.
25
28
:meth:`~Bijectionist.statistics_fibers` | Collect elements with the same statistics.
@@ -1168,7 +1171,7 @@ def set_distributions(self, *elements_distributions):
1168
1171
[([[]], [0]),
1169
1172
([[1]], [1]),
1170
1173
([[1, 2, 3]], [3]),
1171
- ([[2, 1, 3 ]], [2]),
1174
+ ([[2, 3, 1 ]], [2]),
1172
1175
([[1, 2], [2, 1]], [1, 2])]
1173
1176
1174
1177
TESTS:
@@ -1694,7 +1697,10 @@ def merge_until_split():
1694
1697
while True :
1695
1698
solution = merge_until_split ()
1696
1699
if solution is None :
1697
- self .set_constant_blocks (tmp_P )
1700
+ self ._P = tmp_P
1701
+ # recreate the MILP
1702
+ self ._bmilp = _BijectionistMILP (self ,
1703
+ self ._bmilp ._solution_cache )
1698
1704
return
1699
1705
1700
1706
updated_multiple_preimages = defaultdict (list )
@@ -2040,7 +2046,7 @@ def minimal_subdistributions_blocks_iterator(self):
2040
2046
sage: bij.constant_blocks(optimal=True)
2041
2047
{{'a', 'b'}}
2042
2048
sage: list(bij.minimal_subdistributions_blocks_iterator())
2043
- [(['a ', 'a ', 'c', 'd', 'e'], [1, 1, 2, 2, 3])]
2049
+ [(['b ', 'b ', 'c', 'd', 'e'], [1, 1, 2, 2, 3])]
2044
2050
2045
2051
An example with overlapping minimal subdistributions::
2046
2052
@@ -2561,14 +2567,19 @@ class is used to manage the MILP, add constraints, solve the
2561
2567
problem and check for uniqueness of solution values.
2562
2568
2563
2569
"""
2564
- def __init__ (self , bijectionist : Bijectionist ):
2570
+ def __init__ (self , bijectionist : Bijectionist , solutions = None ):
2565
2571
r"""
2566
2572
Initialize the mixed integer linear program.
2567
2573
2568
2574
INPUT:
2569
2575
2570
2576
- ``bijectionist`` -- an instance of :class:`Bijectionist`.
2571
2577
2578
+ - ``solutions`` (optional, default: ``None``) -- a list of
2579
+ solutions of the problem, each provided as a dictionary
2580
+ mapping `(a, z)` to a Boolean, such that at least one
2581
+ element from each block of `P` appears as `a`.
2582
+
2572
2583
.. TODO::
2573
2584
2574
2585
it might be cleaner not to pass the full bijectionist
@@ -2581,6 +2592,7 @@ def __init__(self, bijectionist: Bijectionist):
2581
2592
sage: from sage.combinat.bijectionist import _BijectionistMILP
2582
2593
sage: _BijectionistMILP(bij)
2583
2594
<sage.combinat.bijectionist._BijectionistMILP object at ...>
2595
+
2584
2596
"""
2585
2597
# the attributes of the bijectionist class we actually use:
2586
2598
# _possible_block_values
@@ -2596,15 +2608,15 @@ def __init__(self, bijectionist: Bijectionist):
2596
2608
2597
2609
self .milp = MixedIntegerLinearProgram (solver = bijectionist ._solver )
2598
2610
self .milp .set_objective (None )
2599
- self ._solution_cache = []
2600
2611
indices = [(p , z )
2601
2612
for p , tZ in bijectionist ._possible_block_values .items ()
2602
2613
for z in tZ ]
2603
2614
self ._x = self .milp .new_variable (binary = True , indices = indices )
2604
2615
2605
- for p in _disjoint_set_roots (bijectionist ._P ):
2606
- self .milp .add_constraint (sum (self ._x [p , z ]
2607
- for z in bijectionist ._possible_block_values [p ]) == 1 ,
2616
+ tZ = bijectionist ._possible_block_values
2617
+ P = bijectionist ._P
2618
+ for p in _disjoint_set_roots (P ):
2619
+ self .milp .add_constraint (sum (self ._x [p , z ] for z in tZ [p ]) == 1 ,
2608
2620
name = f"block { p } " [:50 ])
2609
2621
self .add_alpha_beta_constraints ()
2610
2622
self .add_distribution_constraints ()
@@ -2614,6 +2626,12 @@ def __init__(self, bijectionist: Bijectionist):
2614
2626
if get_verbose () >= 2 :
2615
2627
self .show ()
2616
2628
2629
+ self ._solution_cache = []
2630
+ if solutions is not None :
2631
+ for solution in solutions :
2632
+ self ._add_solution ({(P .find (a ), z ): value
2633
+ for (a , z ), value in solution .items ()})
2634
+
2617
2635
def show (self , variables = True ):
2618
2636
r"""
2619
2637
Print the constraints and variables of the MILP together
@@ -2744,8 +2762,9 @@ def solve(self, additional_constraints, solution_index=0):
2744
2762
return_indices = True ))
2745
2763
try :
2746
2764
self .milp .solve ()
2747
- self .last_solution = self .milp .get_values (self ._x ,
2748
- convert = bool , tolerance = 0.1 )
2765
+ # moving this out of the try...finally block breaks SCIP
2766
+ solution = self .milp .get_values (self ._x ,
2767
+ convert = bool , tolerance = 0.1 )
2749
2768
finally :
2750
2769
b = self .milp .get_backend ()
2751
2770
if hasattr (b , "_get_model" ):
@@ -2754,16 +2773,23 @@ def solve(self, additional_constraints, solution_index=0):
2754
2773
m .freeTransform ()
2755
2774
self .milp .remove_constraints (new_indices )
2756
2775
2757
- # veto the solution, by requiring that not all variables with
2758
- # value 1 have value 1 in the new MILP
2776
+ self ._add_solution (solution )
2777
+
2778
+ def _add_solution (self , solution ):
2779
+ r"""
2780
+ Add the ``last_solution`` to the cache and an
2781
+ appropriate constraint to the MILP.
2782
+
2783
+ """
2759
2784
active_vars = [self ._x [p , z ]
2760
2785
for p in _disjoint_set_roots (self ._bijectionist ._P )
2761
2786
for z in self ._bijectionist ._possible_block_values [p ]
2762
- if self . last_solution [(p , z )]]
2787
+ if solution [(p , z )]]
2763
2788
self .milp .add_constraint (sum (active_vars ) <= len (active_vars ) - 1 ,
2764
2789
name = "veto" )
2790
+ self ._solution_cache .append (solution )
2791
+ self .last_solution = solution
2765
2792
2766
- self ._solution_cache .append (self .last_solution )
2767
2793
2768
2794
def _is_solution (self , constraint , values ):
2769
2795
r"""
@@ -2833,9 +2859,11 @@ def solution(self, on_blocks):
2833
2859
{'a': 0, 'c': 0}
2834
2860
2835
2861
"""
2862
+ P = self ._bijectionist ._P
2863
+ tZ = self ._bijectionist ._possible_block_values
2836
2864
mapping = {} # A -> Z or P -> Z, a +-> s(a)
2837
- for p , block in self . _bijectionist . _P .root_to_elements_dict ().items ():
2838
- for z in self . _bijectionist . _possible_block_values [p ]:
2865
+ for p , block in P .root_to_elements_dict ().items ():
2866
+ for z in tZ [p ]:
2839
2867
if self .last_solution [p , z ] == 1 :
2840
2868
if on_blocks :
2841
2869
mapping [p ] = z
@@ -3285,9 +3313,9 @@ def _non_copying_intersection(sets):
3285
3313
([[3, 1, 2]], [3])
3286
3314
([[4, 1, 2, 3]], [4])
3287
3315
([[5, 1, 2, 3, 4]], [5])
3288
- ([[2, 1, 4 , 5, 3 ], [2, 3 , 5, 1, 4 ], [2, 4, 1, 5, 3], [2 , 4, 5, 1, 3 ]], [2, 3, 3, 3])
3289
- ([[2 , 1, 5, 3 , 4], [2, 5, 1, 3, 4 ], [3, 1, 5, 2, 4], [3, 5, 1 , 2, 4 ]], [3, 3, 4, 4])
3290
- ([[1, 3, 2, 5, 4], [1, 3, 5, 2, 4 ], [1, 4, 2, 5, 3 ], [1 , 4, 5 , 2, 3 ], [1, 4 , 5, 3 , 2], [1 , 5, 4, 2 , 3], [1, 5, 4, 3, 2 ]], [2, 2, 3, 3, 3, 3, 3])
3316
+ ([[2, 3, 1 , 5, 4 ], [2, 4 , 5, 3, 1 ], [2, 5, 4, 1, 3], [3 , 4, 1, 5, 2 ]], [2, 3, 3, 3])
3317
+ ([[3 , 1, 2, 5 , 4], [4, 1, 2, 5, 3 ], [3, 5, 2, 1, 4], [4, 1, 5 , 2, 3 ]], [3, 3, 4, 4])
3318
+ ([[2, 1, 3, 5, 4], [2, 4, 1, 3, 5 ], [2, 5, 3, 1, 4 ], [3 , 4, 1 , 2, 5 ], [3, 1 , 5, 4 , 2], [2 , 5, 1, 4 , 3], [2, 1, 5, 4, 3]], [2, 2, 3, 3, 3, 3, 3])
3291
3319
3292
3320
sage: l = list(bij.solutions_iterator()); len(l) # not tested -- (17 seconds with SCIP on AMD Ryzen 5 PRO 3500U w/ Radeon Vega Mobile Gfx)
3293
3321
504
0 commit comments