Commit b798e55b authored by Uwe Köckemann's avatar Uwe Köckemann
Browse files

Merge branch 'master' of gitsvn-nt.oru.se:uwe.kockemann/moving-targets

parents 4e62e5a1 7c2b8df2
......@@ -69,4 +69,7 @@
constraints:{
(> y -30) ;; Each element in set needs to be translated to PySMT
;;(fairness-1 y 1.0)
}))
\ No newline at end of file
}
alpha:1 ;; Parameter alpha
beta:1 ;; Parameter beta
))
\ No newline at end of file
# Input Parameters:
# - n_points -> number of examples.
# - n_classes -> number of classes
import numpy as np
from docplex.mp.model import Model as CPModel
n_points = 10
n_classes = 5
"""
Variable matrix of the type
Z = [
[ 0, 0, 0, 1, 0],
[ 1, 0, 0, 0, 0],
[ 0, 1, 0, 0, 0],
...
[ 1, 0, 0, 0, 0]
]
"""
# Build a model
mod = CPModel('Class balancer')
# Define the (binary) variables.
Z = mod.binary_var_matrix(keys1=n_points,
keys2=n_classes,
name='z')
# Each example has to be assigned to one class.
for i in range(n_points):
xpr = mod.sum(Z[i, c] for c in range(n_classes))
mod.add_constraint(xpr == 1)
# Balance constraint.
print("Adding Balance Constraint")
B = int(np.ceil(n_points / n_classes))
for c in range(n_classes):
xpr = mod.sum([Z[i, c] for i in range(n_points)])
mod.add_constraint(xpr <= B)
# Define the loss w.r.t. the true labels
# p_loss = (1 / n_points) * mod.sum([(1 - Z[i, p[i]]) for i in range(n_points)])
# y_loss = (1 / n_points) * mod.sum([(1 - Z[i, y[i]]) for i in range(n_points)])
# Solve the problem
sol = mod.solve()
if sol:
sat = mod.get_solve_status()
print("Status: " + str(sat))
zarr = [sum(c * sol.get_value(Z[i, c])
for c in range(n_classes)) for i in range(n_points)]
# Z_sol = np.array([int(v) for v in zarr])
Z_sol = np.array([
[sol.get_value(Z[i, c]) for c in range(n_classes)] for i in range(n_points)
], dtype=int)
print(Z_sol)
else:
print("No solution found")
# Input Parameters:
# - n_points -> number of examples.
# - n_classes -> number of classes
import numpy as np
from pysmt.shortcuts import Symbol, LE, GE, Int, And, Equals, Plus, is_sat, get_model
from pysmt.typing import INT
n_points = 10
n_classes = 5
"""
Variable matrix of the type
Z = [
[ 0, 0, 0, 1, 0],
[ 1, 0, 0, 0, 0],
[ 0, 1, 0, 0, 0],
...
[ 1, 0, 0, 0, 0]
]
"""
Z = [[Symbol("z_%d%d" % (i, j), INT) for i in range(n_classes)]
for j in range(n_points)]
# Define the variable's domain.
bool_domain = And(And(And(GE(z, Int(0)),
LE(z, Int(1))) for z in Z[i]) for i in range(n_points))
# Each example has to be assigned to one class.
class_domain = And(Equals(Int(1), Plus(Z[i])) for i in range(n_points))
# Balance constraint.
print("Adding Balance Constraint")
B = int(np.ceil(n_points / n_classes))
bal_domain = And(LE(Plus([Z[i][j] for i in range(n_points)]), Int(B)) for j in range(n_classes))
formula = And(And(bal_domain, class_domain), bool_domain)
# print("Serialization of the formula:")
# print(formula)
model = get_model(formula)
if model:
# print(model)
sat = is_sat(formula, solver_name='msat')
print("SAT: " + str(sat))
Z_sol = np.array([[model.get_value(Z[j][i]) for i in range(n_classes)] for j in range(n_points)])
print(Z_sol)
else:
print("No solution found")
import numpy as np
from abc import ABC, abstractmethod
class AbstractConstraint(ABC):
@abstractmethod
def is_satisfied(self, x, y):
pass
class InequalityGlobalConstraint(AbstractConstraint):
def __init__(self, name, sense, rhs):
self.name = name
self.sense = sense
self.rhs = rhs
if sense == '<=':
self._checker = self.leq
elif sense == '>=':
self._checker = self.geq
else:
raise ValueError("Sense " + str(sense) + " not understood!")
def leq(self, value):
return value <= self.rhs
def geq(self, value):
return value >= self.rhs
def is_satisfied(self, x, y):
c = np.apply_along_axis(self._checker, axis=0, arr=y)
return np.all(c)
class FairnessConstraint(AbstractConstraint):
# @todo
def is_satisfied(self, x, y):
pass
......@@ -12,55 +12,47 @@ class MovingTarget(ABC):
self.n = n
def apply(self, args):
y_s = args[Symbolic("data")]
d = args[Symbolic("examples")][Symbolic("data")] # d = (x_s, y_s)
L = args[Symbolic("loss-function")]
C = args[Symbolic("constraints")]
alpha = args[Symbolic("alpha")]
beta = args[Symbolic("beta")]
# initial step.
M = self.initialize_ext()
M = self.initialize_ext(d)
self.add_constraints(M, C, y_s)
self.set_loss_function(M, L)
self.add_constraints(M, C, d)
# self.set_loss_function(M, L, d)
# d_data = self.get_pysmt_data(y_k)
y_k = self.ML.get_function().apply(y_s)
y_k = self.ML.get_function().apply(d)
for i in range(0, self.n):
self.set_m_alpha(M, y_k)
self.set_m_beta(M, y_k)
sat_C = self.check_constraints_ext(M, C, d)
sat_C = self.check_constraints_ext(M)
if sat_C:
self.m_alpha(M, y_k)
if not sat_C:
self.m_alpha(M, L, y_k, alpha)
else:
self.set_m_beta(M, y_k)
self.m_beta(M, L, y_k, beta)
z_k = self.solve_ext(M)
ml_problem = self.assemble_ml_problem(args, z_k)
y_k = self.ML.apply(ml_problem)
@abstractmethod
def set_loss_function(self, M, lf):
"""Symbolic term (or tuple in case of parameters) to loss function."""
@abstractmethod
def add_constraints(self, M, C, y_s):
"""Convert collection of constraint expressions."""
@abstractmethod
def add_data(self, M, y_s):
"""Convert data and add to M."""
@abstractmethod
def m_alpha(self, M):
def m_alpha(self, M, L, y_k, alpha):
"""Get m_alpha part."""
@abstractmethod
def m_beta(self, M):
def m_beta(self, M, L, y_k, beta):
"""Get m_beta part."""
@abstractmethod
def check_constraints_ext(self, M):
def check_constraints_ext(self, M, C, d):
"""Call external solver to test if constraints are satsified."""
@abstractmethod
......@@ -68,7 +60,7 @@ class MovingTarget(ABC):
"""Solve problem with external solver and return updated AIDDL data."""
@abstractmethod
def initialize_ext(self):
def initialize_ext(self, d):
"""Initialize external solver and return blank model."""
def assemble_ml_problem(self, current, y_k):
......
import numpy as np
from moving_target_abc import MovingTarget
from docplex.mp.model import Model as CPModel
from docplex.mp.model import DOcplexException
from constraint import InequalityGlobalConstraint
class MovingTargetRegCplex(MovingTarget):
class MovingTargetCplex(MovingTarget):
x = None
y = None
variables = list()
constraints = list()
""" REMOVED FOR NOW
def set_loss_function(self, M, lf):
loss_function = 0
if lf == 'MSE' or lf == 'MeanSquaredError':
# (declare variables and loss)
pass
else:
raise NotImplementedError("Loss function not recognized!")
M.minimize(loss_function)
pass
"""
def add_constraints(self, M, C, y_s):
pass
def add_constraints(self, M: CPModel, C, y_s):
"""
Constraints are expressed with a list/set? of tuples, i.e.:
C = [
('>', y, -30),
('fairness', y, 1.0),
]
"""
for c in C:
ctype, cvar, cval = c
def add_data(self, M, y_s):
pass
# Store the constraint in the class.
cstr = InequalityGlobalConstraint('ct', ctype, cval)
self.constraints.append(cstr)
def m_alpha(self, M):
pass
# Translate the constraint to CPLEX language.
# todo: indexing of variables contraint <-> model
x = [M.get_var_by_name(v) for v in self.variables]
if ctype == '>=':
M.add_constraints([_x >= cval for _x in x])
elif ctype == '<=':
M.add_constraints([_x <= cval for _x in x])
else:
raise NotImplementedError("Constraint type not recognized " + str(ctype))
def m_beta(self, M):
pass
print("Constraint added: " + str(c))
def check_constraints_ext(self, M):
pass
def m_alpha(self, M, L, y_k, alpha):
n_points = len(y_k)
idx_var = [i for i in range(n_points)]
x = [M.get_var_by_name(s) for s in self.variables]
y_k = y_k.flatten()
if L == 'MSE' or L == 'MeanSquaredError':
y_loss = (1.0 / n_points) * M.sum([(self.y[i] - x[i]) * (self.y[i] - x[i]) for i in idx_var])
p_loss = (1.0 / n_points) * M.sum([(y_k[i] - x[i]) * (y_k[i] - x[i]) for i in idx_var])
elif L == 'MAE' or L == 'MeanAbsoluteError':
y_loss = (1.0 / n_points) * M.sum([M.abs(self.y[i] - x[i]) for i in idx_var])
p_loss = (1.0 / n_points) * M.sum([M.abs(y_k[i] - x[i]) for i in idx_var])
else:
raise NotImplementedError("Loss function not recognized!")
obj_func = y_loss + (1.0 / alpha) * p_loss
M.minimize(obj_func)
def m_beta(self, M, L, y_k, beta):
n_points = len(y_k)
idx_var = [i for i in range(n_points)]
x = [M.get_var_by_name(s) for s in self.variables]
y_k = y_k.flatten()
if L == 'MSE' or L == 'MeanSquaredError':
y_loss = (1.0 / n_points) * M.sum([(self.y[i] - x[i]) * (self.y[i] - x[i]) for i in idx_var])
p_loss = (1.0 / n_points) * M.sum([(y_k[i] - x[i]) * (y_k[i] - x[i]) for i in idx_var])
elif L == 'MAE' or L == 'MeanAbsoluteError':
y_loss = (1.0 / n_points) * M.sum([M.abs(self.y[i] - x[i]) for i in idx_var])
p_loss = (1.0 / n_points) * M.sum([M.abs(y_k[i] - x[i]) for i in idx_var])
else:
raise NotImplementedError("Loss function not recognized!")
obj_func = y_loss
M.add(p_loss <= beta)
M.minimize(obj_func)
def check_constraints_ext(self, M, C, d):
sat = True
for c in self.constraints:
sat = sat & c.is_satisfied(*d)
print("Constraint satisfaction: " + str(sat))
return sat
def solve_ext(self, M):
pass
def initialize_ext(self):
pass
M.solve()
# Check solution.
self._check_solution(M)
# Retrieve solution.
x = [M.get_var_by_name(s) for s in self.variables]
y_opt = np.array([_x.solution_value for _x in x])
return y_opt
def initialize_ext(self, d, name='cplex_model'):
x, y = d
# Store the input data.
self.x = x
self.y = y.flatten()
# Model declaration.
mod = CPModel(name)
# Variable declaration.
n_points = len(y)
idx_var = [i for i in range(n_points)]
mod.continuous_var_list(keys=idx_var, lb=-10.0, ub=10.0, name='y')
# Store variable names.
self.variables = ['y_%d' %i for i in idx_var]
print("Variables added: " + str(self.variables))
return mod
@staticmethod
def _check_solution(mod):
try:
mod.check_has_solution()
print("Solver status: " + mod.solve_details.status)
print("MIP Gap: {:.3f} %".format(100 * mod.solve_details.mip_relative_gap))
except DOcplexException as err:
# self.logger.error("Model infeasible!")
mod.export_as_lp(path='./', basename='infeasible')
raise err
if __name__ == '__main__':
# Generation of synthetic data.
d = 10 * np.random.rand(10, 1), 10 * np.random.rand(10, 1)
# Loss function.
L = 'MSE'
# Costraints declaration.
C = [
("<=", 'y', 1.2),
(">=", 'y', 0.3),
]
# Params.
alpha = 1
beta = 1
print("Original data: ")
x, y = d
for i in range(len(x)):
print("%.2f \t %.2f" % (x[i], y[i]))
m = MovingTargetRegCplex(n=1)
# CPLEX model initialization.
cplex_mod = m.initialize_ext(d)
# Add constraints.
m.add_constraints(cplex_mod, C, y)
# Check constraint satisfaction.
sat = m.check_constraints_ext(cplex_mod, C, d)
if not sat:
# Alpha step.
m.m_alpha(cplex_mod, L, y, beta)
else:
# Beta step.
m.m_beta(cplex_mod, L, y, beta)
# Model solving.
y_new = m.solve_ext(cplex_mod)
print("Final data: ")
for i in range(len(x)):
print("%.2f \t %.2f" % (x[i], y_new[i]))
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