Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

r""" 

Group-Divisible Designs (GDD) 

 

This module gathers everything related to Group-Divisible Designs. The 

constructions defined here can be accessed through ``designs.<tab>``:: 

 

sage: designs.group_divisible_design(14,{4},{2}) 

Group Divisible Design on 14 points of type 2^7 

 

The main function implemented here is :meth:`group_divisible_design` (which 

calls all others) and the main class is :class:`GroupDivisibleDesign`. The 

following functions are available: 

 

.. csv-table:: 

:class: contentstable 

:widths: 30, 70 

:delim: | 

 

:func:`group_divisible_design` | Return a `(v,K,G)`-Group Divisible Design. 

:func:`GDD_4_2` | Return a `(2q,\{4\},\{2\})`-GDD for `q` a prime power with `q\equiv 1\pmod{6}`. 

 

Functions 

--------- 

""" 

from __future__ import absolute_import, division 

 

#***************************************************************************** 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 2 of the License, or 

# (at your option) any later version. 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

from six.moves import range 

 

from sage.arith.all import is_prime_power 

from sage.misc.unknown import Unknown 

from .incidence_structures import IncidenceStructure 

 

def group_divisible_design(v,K,G,existence=False,check=False): 

r""" 

Return a `(v,K,G)`-Group Divisible Design. 

 

A `(v,K,G)`-GDD is a pair `(\mathcal G, \mathcal B)` where: 

 

- `\mathcal G` is a partition of `X=\bigcup \mathcal G` where `|X|=v` 

 

- `\forall S\in \mathcal G, |S| \in G` 

 

- `\forall S\in \mathcal B, |S| \in K` 

 

- `\mathcal G\cup \mathcal B` is a `(v,K\cup G)`-PBD 

 

For more information, see the documentation of 

:class:`~sage.combinat.designs.incidence_structures.GroupDivisibleDesign` or 

:class:`~sage.combinat.designs.bibd.PairwiseBalancedDesign`. 

 

INPUT: 

 

- ``v`` (integer) 

 

- ``K,G`` (sets of integers) 

 

- ``existence`` (boolean) -- instead of building the design, return: 

 

- ``True`` -- meaning that Sage knows how to build the design 

 

- ``Unknown`` -- meaning that Sage does not know how to build the 

design, but that the design may exist (see :mod:`sage.misc.unknown`). 

 

- ``False`` -- meaning that the design does not exist. 

 

- ``check`` -- (boolean) Whether to check that output is correct before 

returning it. As this is expected to be useless (but we are cautious 

guys), you may want to disable it whenever you want speed. Set to ``True`` 

by default. 

 

.. NOTE:: 

 

The GDD returned by this function are defined on ``range(v)``, and its 

groups are sets of consecutive integers. 

 

EXAMPLES:: 

 

sage: designs.group_divisible_design(14,{4},{2}) 

Group Divisible Design on 14 points of type 2^7 

""" 

G = list(set(G)) 

K = list(set(K)) 

 

blocks = None 

 

# from a (v+1,k,1)-BIBD 

if (len(G) == 1 and 

len(K) == 1 and 

G[0]+1 in K): 

from .bibd import balanced_incomplete_block_design 

k = K[0] 

if existence: 

return balanced_incomplete_block_design(v+1,k,existence=True) 

BIBD = balanced_incomplete_block_design(v+1,k) 

groups = [[x for x in S if x!=v] for S in BIBD if v in S] 

d = {p:i for i,p in enumerate(sum(groups,[]))} 

d[v]=v 

BIBD.relabel(d) 

groups = [list(range((k-1)*i,(k-1)*(i+1))) for i in range(v//(k-1))] 

blocks = [S for S in BIBD if v not in S] 

 

# (v,{4},{2})-GDD 

elif (v%2==0 and 

K == [4] and 

G == [2] and 

GDD_4_2(v//2,existence=True)): 

if existence: 

return True 

return GDD_4_2(v//2,check=check) 

 

# From a TD(k,g) 

elif (len(G) == 1 and 

len(K) == 1 and 

K[0]*G[0] == v): 

from .orthogonal_arrays import transversal_design 

return transversal_design(k=K[0],n=G[0],existence=existence) 

 

if blocks: 

return GroupDivisibleDesign(v, 

groups = groups, 

blocks = blocks, 

G = G, 

K = K, 

check = check, 

copy = True) 

 

if existence: 

return Unknown 

raise NotImplementedError 

 

def GDD_4_2(q,existence=False,check=True): 

r""" 

Return a `(2q,\{4\},\{2\})`-GDD for `q` a prime power with `q\equiv 1\pmod{6}`. 

 

This method implements Lemma VII.5.17 from [BJL99] (p.495). 

 

INPUT: 

 

- ``q`` (integer) 

 

- ``existence`` (boolean) -- instead of building the design, return: 

 

- ``True`` -- meaning that Sage knows how to build the design 

 

- ``Unknown`` -- meaning that Sage does not know how to build the 

design, but that the design may exist (see :mod:`sage.misc.unknown`). 

 

- ``False`` -- meaning that the design does not exist. 

 

- ``check`` -- (boolean) Whether to check that output is correct before 

returning it. As this is expected to be useless (but we are cautious 

guys), you may want to disable it whenever you want speed. Set to ``True`` 

by default. 

 

EXAMPLES:: 

 

sage: from sage.combinat.designs.group_divisible_designs import GDD_4_2 

sage: GDD_4_2(7,existence=True) 

True 

sage: GDD_4_2(7) 

Group Divisible Design on 14 points of type 2^7 

sage: GDD_4_2(8,existence=True) 

Unknown 

sage: GDD_4_2(8) 

Traceback (most recent call last): 

... 

NotImplementedError 

""" 

if q <=1 or q%6 != 1 or not is_prime_power(q): 

if existence: 

return Unknown 

raise NotImplementedError 

if existence: 

return True 

 

from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF 

G = GF(q,'x') 

w = G.primitive_element() 

e = w**((q - 1) // 3) 

 

# A first parallel class is defined. G acts on it, which yields all others. 

first_class = [[(0,0),(1,w**i),(1,e*w**i),(1,e*e*w**i)] 

for i in range((q - 1) // 6)] 

 

label = {p:i for i,p in enumerate(G)} 

classes = [[[2*label[x[1]+g]+(x[0]+j)%2 for x in S] 

for S in first_class] 

for g in G for j in range(2)] 

 

return GroupDivisibleDesign(2*q, 

groups = [[i,i+1] for i in range(0,2*q,2)], 

blocks = sum(classes,[]), 

K = [4], 

G = [2], 

check = check, 

copy = False) 

 

class GroupDivisibleDesign(IncidenceStructure): 

r""" 

Group Divisible Design (GDD) 

 

Let `K` and `G` be sets of positive integers and let `\lambda` be a positive 

integer. A Group Divisible Design of index `\lambda` and order `v` is a 

triple `(V,\mathcal G,\mathcal B)` where: 

 

- `V` is a set of cardinality `v` 

 

- `\mathcal G` is a partition of `V` into groups whose size belongs to `G` 

 

- `\mathcal B` is a family of subsets of `V` whose size belongs to `K` such 

that any two points `p_1,p_2\in V` from different groups appear 

simultaneously in exactly `\lambda` elements of `\mathcal B`. Besides, a 

group and a block intersect on at most one point. 

 

If `K=\{k_1,...,k_l\}` and `G` has exactly `m_i` groups of cardinality `k_i` 

then `G` is said to have type `k_1^{m_1}...k_l^{m_l}`. 

 

INPUT: 

 

- ``points`` -- the underlying set. If ``points`` is an integer `v`, then 

the set is considered to be `\{0, ..., v-1\}`. 

 

- ``groups`` -- the groups of the design. Set to ``None`` for an automatic 

guess (this triggers ``check=True`` and can thus cost some time). 

 

- ``blocks`` -- collection of blocks 

 

- ``G`` -- list of integers of which the sizes of the groups must be 

elements. Set to ``None`` (automatic guess) by default. 

 

- ``K`` -- list of integers of which the sizes of the blocks must be 

elements. Set to ``None`` (automatic guess) by default. 

 

- ``lambd`` (integer) -- value of `\lambda`, set to `1` by default. 

 

- ``check`` (boolean) -- whether to check that the design is indeed a `GDD` 

with the right parameters. Set to ``True`` by default. 

 

- ``copy`` -- (use with caution) if set to ``False`` then ``blocks`` must be 

a list of lists of integers. The list will not be copied but will be 

modified in place (each block is sorted, and the whole list is 

sorted). Your ``blocks`` object will become the instance's internal data. 

 

EXAMPLES:: 

 

sage: from sage.combinat.designs.group_divisible_designs import GroupDivisibleDesign 

sage: TD = designs.transversal_design(4,10) 

sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] 

sage: GDD = GroupDivisibleDesign(40,groups,TD); GDD 

Group Divisible Design on 40 points of type 10^4 

 

With unspecified groups:: 

 

sage: D = designs.transversal_design(4,3).relabel(list('abcdefghiklm'),inplace=False).blocks() 

sage: GDD = GroupDivisibleDesign('abcdefghiklm',None,D) 

sage: sorted(GDD.groups()) 

[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'], ['k', 'l', 'm']] 

 

""" 

def __init__(self, points, groups, blocks, G=None, K=None, lambd=1, check=True, copy=True,**kwds): 

r""" 

Constructor function 

 

EXAMPLES:: 

 

sage: from sage.combinat.designs.group_divisible_designs import GroupDivisibleDesign 

sage: TD = designs.transversal_design(4,10) 

sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] 

sage: GDD = GroupDivisibleDesign(40,groups,TD); GDD 

Group Divisible Design on 40 points of type 10^4 

""" 

from .designs_pyx import is_group_divisible_design 

 

self._lambd = lambd 

 

IncidenceStructure.__init__(self, 

points, 

blocks, 

copy=copy, 

check=False, 

**kwds) 

 

if (groups is None or 

(copy is False and self._point_to_index is None)): 

self._groups = groups 

elif self._point_to_index is None: 

self._groups = [g[:] for g in groups] 

else: 

self._groups = [[self._point_to_index[x] for x in g] for g in groups] 

 

if check or groups is None: 

is_gdd = is_group_divisible_design(self._groups,self._blocks,self.num_points(),G,K,lambd,verbose=1) 

assert is_gdd 

if groups is None: 

self._groups = is_gdd[1] 

 

def groups(self): 

r""" 

Return the groups of the Group-Divisible Design. 

 

EXAMPLES:: 

 

sage: from sage.combinat.designs.group_divisible_designs import GroupDivisibleDesign 

sage: TD = designs.transversal_design(4,10) 

sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] 

sage: GDD = GroupDivisibleDesign(40,groups,TD); GDD 

Group Divisible Design on 40 points of type 10^4 

sage: GDD.groups() 

[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 

[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], 

[20, 21, 22, 23, 24, 25, 26, 27, 28, 29], 

[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]] 

 

TESTS: 

 

Non-integer ground set:: 

 

sage: TD=designs.transversal_design(5,5) 

sage: TD.relabel({i:chr(97+i) for i in range(25)}) 

sage: TD.groups() 

[['a', 'b', 'c', 'd', 'e'], 

['f', 'g', 'h', 'i', 'j'], 

['k', 'l', 'm', 'n', 'o'], 

['p', 'q', 'r', 's', 't'], 

['u', 'v', 'w', 'x', 'y']] 

""" 

if self._point_to_index is None: 

return [list(g) for g in self._groups] 

else: 

return [[self._points[i] for i in g] for g in self._groups] 

 

def __repr__(self): 

r""" 

Returns a string that describes self 

 

EXAMPLES:: 

 

sage: from sage.combinat.designs.group_divisible_designs import GroupDivisibleDesign 

sage: TD = designs.transversal_design(4,10) 

sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] 

sage: GDD = GroupDivisibleDesign(40,groups,TD); GDD 

Group Divisible Design on 40 points of type 10^4 

""" 

 

group_sizes = [len(_) for _ in self._groups] 

 

gdd_type = ["{}^{}".format(s,group_sizes.count(s)) 

for s in sorted(set(group_sizes))] 

gdd_type = ".".join(gdd_type) 

 

if not gdd_type: 

gdd_type = "1^0" 

 

v = self.num_points() 

 

return "Group Divisible Design on {} points of type {}".format(v,gdd_type)