Commit 7e5d3ac1 authored by s.gretchko's avatar s.gretchko
Browse files

Added Sudoku experiment and Sudoku_DRl demo

parent eda7f667
import SetProxPythonPath
from proxtoolbox.experiments.sudoku.sudokuExperiment import SudokuExperiment
# if a puzzle is not specified, the sudoku experiment uses the
# default puzzle, which is the same as the one below:
puzzle = ((2,0,0,0,0,1,0,3,0),
(4,0,0,0,8,6,1,0,0),
(0,0,0,0,0,0,0,0,0),
(0,0,0,0,1,0,0,0,0),
(0,0,0,0,0,0,9,0,0),
(0,0,5,0,0,3,0,0,7),
(0,0,0,0,0,0,0,0,0),
(1,0,0,0,0,7,4,9,0),
(0,2,4,1,0,0,0,0,0))
Sudoku = SudokuExperiment(algorithm='DRl', sudoku=puzzle)
Sudoku.run()
Sudoku.show()
......@@ -265,6 +265,8 @@ class Experiment(metaclass=ExperimentMetaClass):
print("Took", self.output['stats']['iter'], "iterations and",
self.output['stats']['time'], "seconds.")
self.postprocess()
if self.save_output:
self.saveOutput()
......@@ -356,8 +358,10 @@ class Experiment(metaclass=ExperimentMetaClass):
self.productProxOperators = proxOperatorClasses
# and propagator and inverse propagator
self.propagator = getattr(proxoperators, self.propagator)
self.inverse_propagator = getattr(proxoperators, self.inverse_propagator)
if self.propagator is not None:
self.propagator = getattr(proxoperators, self.propagator)
if self.inverse_propagator is not None:
self.inverse_propagator = getattr(proxoperators, self.inverse_propagator)
def reshapeData(self, Nx, Ny, Nz):
"""
......@@ -389,7 +393,7 @@ class Experiment(metaclass=ExperimentMetaClass):
# The following lines of code correspond to the Cone_and_Sphere branch. However,
# the case where the data is a cell is not treated (in Matlab implementation)
# TODO: Need to check is this is OK.
if not isCell(self.data):
if self.data is not None and not isCell(self.data):
tmp = self.data.shape
if tmp[0] == 1 or tmp[1] == 1:
self.data_sq = self.data_sq.reshape(Nx, Ny)
......@@ -415,6 +419,12 @@ class Experiment(metaclass=ExperimentMetaClass):
accelerator = None
self.algorithm = algorithmClass(self, iterateMonitor, accelerator)
def postprocess(self):
"""
Optional processing of the data returned by the algorithm. Default
implementation does nothing. May be overriden by derived classes.
"""
pass
def saveOutput(self):
......
# pylint: disable=no-member # for dynamically created variables
# pylint: disable=access-member-before-definition # for dynamically created variables
from proxtoolbox.experiments.experiment import Experiment
from proxtoolbox import proxoperators
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.pyplot import subplots, show, figure
class SudokuExperiment(Experiment):
'''
Sudoku experiment class
The goal of a standard Sudoku puzzle is to fill a 9x9 array
with the numbers from 1 to 9. Every row, every column and each
of the 9 3x3 subblocks should contain each number only once.
Starting point is a grid that already contains several numbers.
Usually, there exists a unique solution.
'''
@staticmethod
def getDefaultParameters():
defaultParams = {
'experiment_name' : 'Sudoku',
'Nx':9,
'Ny':9,
'Nz':9,
'product_space_dimension':4,
'algorithm': 'DRl',
'MAXIT': 2000,
'TOL': 1e-9,
'TOL2': 1e-4,
'lambda_0': 1,
'lambda_max': 1,
'lambda_switch': 1,
'norm_data':81,
'sudoku':((2,0,0,0,0,1,0,3,0),
(4,0,0,0,8,6,1,0,0),
(0,0,0,0,0,0,0,0,0),
(0,0,0,0,1,0,0,0,0),
(0,0,0,0,0,0,9,0,0),
(0,0,5,0,0,3,0,0,7),
(0,0,0,0,0,0,0,0,0),
(1,0,0,0,0,7,4,9,0),
(0,2,4,1,0,0,0,0,0)),
'diagnostic': True
}
return defaultParams
def __init__(self, sudoku = None, norm_data=81, **kwargs):
"""
"""
# call parent's __init__ method
super(SudokuExperiment, self).__init__(**kwargs)
# do here any data member initialization
self.norm_data = norm_data
if sudoku is None:
defaultParams = SudokuExperiment.getDefaultParameters()
self.sudoku = defaultParams['sudoku']
self.sudoku = np.array(sudoku, dtype=np.float32)
def loadData(self):
"""
Create the initial iterate based on the given Sudoku puzzle.
"""
u0 = np.zeros((self.Nx, self.Ny, self.Nz, self.product_space_dimension),
dtype=self.sudoku.dtype)
for x in range(self.Nx):
for y in range(self.Ny):
z = int(self.sudoku[x][y]-1)
if z >= 0:
u0[x,y,z,:] = 1
self.u0 = u0
def reshapeData(self, Nx, Ny, Nz):
# override reshapeData as this does not make any sense in this context
pass
def setupProxOperators(self):
"""
Determine the prox operators to be used for this experiment
"""
super(SudokuExperiment, self).setupProxOperators() # call parent's method
self.nProx = 2
self.proxOperators = ['Prox_product_space', 'P_diag']
self.n_product_Prox = self.product_space_dimension
self.productProxOperators = ['ProjRow','ProjColumn','ProjSquare','ProjGiven']
def postprocess(self):
"""
Process the data returned by the algorithm
"""
solution = np.zeros_like(self.sudoku)
u2 = self.algorithm.prox2.eval(self.output['u'])
u1 = self.algorithm.prox1.eval(u2)
A = u1[:,:,:,0]
for x in range(self.Nx):
for y in range(self.Ny):
for z in range(self.Nz):
if A[x,y,z] > 0:
solution[x,y] = z+1
break
self.solution = solution
def show(self):
"""
Generates graphical output from the solution
"""
fig = plt.figure('Sudoku', figsize = (self.figure_width, self.figure_height),
dpi = self.figure_dpi)
# plot plain Sudoku puzzle
ax = plt.subplot(2,2,1)
ax.title.set_text('Given Sudoku')
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
table = ax.table(cellText=self.sudoku.astype(np.int32),
loc='center', cellLoc='center')
fontSize = 15
table.set_fontsize(fontSize)
for cell in table.properties()['child_artists']:
cell.set_height(0.1)
cell.set_width(0.1)
txt = cell.get_text()
if txt.get_text() == '0':
txt.set_text('')
# plot solution
ax = plt.subplot(2,2,2)
ax.title.set_text('Solution')
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
table = ax.table(cellText=self.solution.astype(np.int32),
loc='center', cellLoc='center')
table.set_fontsize(fontSize)
for cell in table.properties()['child_artists']:
cell.set_height(0.1)
cell.set_width(0.1)
# plot the change from one iterate to the next
stats = self.output['stats']
changes = stats['changes']
ax = plt.subplot(2,2,3)
ax.xaxis.label.set_text('Iterations')
ax.yaxis.label.set_text('Change')
plt.semilogy(changes)
# plot the gap
if 'gaps' in stats:
gaps = stats['gaps']
ax = plt.subplot(2,2,4)
ax.xaxis.label.set_text('Iterations')
ax.yaxis.label.set_text('Gap')
plt.semilogy(gaps)
fig.tight_layout()
plt.show()
......@@ -20,3 +20,4 @@ from .P_Sparsity import *
from .P_M import *
from .propagators import *
from .ptychographyProx import *
from .sudokuProx import *
from proxtoolbox.proxoperators.proxoperator import ProxOperator
from proxtoolbox import proxoperators
import numpy as np
# Prox operators used by Sudoku experiment
class SudokuProx(ProxOperator):
"""
Projection onto the given entries in a Sudoku problem
"""
def __init__(self, experiment):
self.given = experiment.sudoku.copy()
self.Nx = experiment.Nx
self.Ny = experiment.Ny
self.Nz = experiment.Nz
class ProjGiven(SudokuProx):
"""
Projection onto the given entries in a Sudoku problem
"""
def __init__(self, experiment):
super(ProjGiven, self).__init__(experiment)
self.given = experiment.sudoku.copy()
def eval(self, u, prox_idx=None):
"""
Applies the prox operator to the input data
Parameters
----------
u : array-like
Iterate.
prox_idx : int, optional
Index of this prox operator
Returns
-------
A : array-like
The projection.
"""
A = np.zeros((self.Nx,self.Ny,self.Nz),dtype=u.dtype)
for x in range(self.Nx):
for y in range(self.Ny):
z = int(self.given[x,y])
if z > 0:
A[x,y,z-1] = 1
else:
A[x,y,np.argmax(u[x,y,:])] = 1
return A
class ProjSquare(SudokuProx):
"""
Projection onto the given entries in a Sudoku problem
"""
def __init__(self, experiment):
super(ProjSquare, self).__init__(experiment)
def eval(self, u, prox_idx=None):
"""
Applies the prox operator to the input data
Parameters
----------
u : array-like
Iterate.
prox_idx : int, optional
Index of this prox operator
Returns
-------
Q : array-like
The projection.
"""
Q = np.zeros((self.Nx,self.Ny,self.Nz), dtype=u.dtype)
for z in range(self.Nz):
for x in range(0,9,3):
for y in range(0,9,3):
v = np.argmax(u[x:(x+3),y:(y+3),z],axis=0)
w = np.argmax(np.amax(u[x:(x+3),y:(y+3),z],axis=0))
Q[x+v[w],y+w,z] = 1
return Q
class ProjColumn(SudokuProx):
"""
Projection onto the given entries in a Sudoku problem
"""
def __init__(self, experiment):
super(ProjColumn, self).__init__(experiment)
def eval(self, u, prox_idx=None):
"""
Applies the prox operator to the input data
Parameters
----------
u : array-like
Iterate.
prox_idx : int, optional
Index of this prox operator
Returns
-------
C : array-like
The projection.
"""
C = np.zeros((self.Nx,self.Ny,self.Nz), dtype=u.dtype)
for x in range(self.Nx):
for z in range(self.Nz):
y = np.argmax(u[x,:,z])
C[x,y,z] = 1
return C
class ProjRow(SudokuProx):
"""
Projection onto the given entries in a Sudoku problem
"""
def __init__(self, experiment):
super(ProjRow, self).__init__(experiment)
def eval(self, u, prox_idx=None):
"""
Applies the prox operator to the input data
Parameters
----------
u : array-like
Iterate.
prox_idx : int, optional
Index of this prox operator
Returns
-------
C : array-like
The projection.
"""
R = np.zeros((self.Nx,self.Ny,self.Nz), dtype=u.dtype)
for y in range(self.Ny):
for z in range(self.Nz):
x = np.argmax(u[:,y,z])
R[x,y,z] = 1
return R
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment