Skip to content

Commit

Permalink
"limitation" is available.
Browse files Browse the repository at this point in the history
- Using the coefficients co_a and co_b, you can make it search only the range that meets the following constraint formula.
---
co_a[1,1]*x_[1] + co_a[1,2]*x_[2] + ... + co_a[1,n]*x_[n] + co_b[1] > 0
co_a[2,1]*x_[1] + co_a[2,2]*x_[2] + ... + co_a[2,n]*x_[n] + co_b[2] > 0
...
co_a[m,1]*x_[1] + co_a[m,2]*x_[2] + ... + co_a[m,n]*x_[n] + co_b[m] > 0
---
- co_a and co_b can be defined in option [runner.limitation] in the toml file.
- When setting constraint formulas, both co_a and co_b must be set in the toml file.
- If neither co_a nor co_b are set in the toml file, the calculation can be performed without constraint formulas.
- If the initial_list set in [algorithm.param] does not fit the constraint formulas, an error occurs.
- If initial_list is not set in [algorithm.param], then initial coordinates that fit the constraint formulas are created.
  • Loading branch information
H-Iwamoto-research committed Sep 28, 2023
1 parent da948bc commit 168da7d
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 13 deletions.
66 changes: 58 additions & 8 deletions src/py2dmat/_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import py2dmat
import py2dmat.util.read_matrix
import py2dmat.util.mapping
import py2dmat.util.limitation
from py2dmat.exception import InputError

# type hints
Expand Down Expand Up @@ -141,6 +142,7 @@ def __init__(
solver: py2dmat.solver.SolverBase,
info: Optional[py2dmat.Info] = None,
mapping=None,
limitation=None,
):
"""
Expand Down Expand Up @@ -178,21 +180,69 @@ def __init__(
self.mapping = py2dmat.util.mapping.Affine(A=A, b=b)
else:
self.mapping = mapping

self.ndim = info.base["dimension"]
if limitation is None:
info_limitation = info.runner.get("limitation",{})
co_a: Optional[np.ndarray] = py2dmat.util.read_matrix.read_matrix(
info_limitation.get("co_a", [])
)
co_b: Optional[np.ndarray] = py2dmat.util.read_matrix.read_matrix(
info_limitation.get("co_b", [])
)
if co_a is not None:
if co_a.size == 0:
co_a = None
else:
if co_a.ndim != 2:
raise InputError("co_a should be a matrix")
if co_a.shape[1] != self.ndim:
msg ='The number of columns in co_a should be equal to'
msg+='the value of "dimension" in the [base] section'
raise InputError(msg)
n_row_co_a = co_a.shape[0]
if co_b is not None:
if co_b.size == 0:
if co_a is None:
co_b = None
else: # co_a is not None:
msg = "ERROR: co_a is defined but co_b is not."
raise InputError(msg)
elif co_b.ndim == 2:
if co_a is not None:
if co_b.shape[0] == 1 or co_b.shape[1] == 1:
co_b = co_b.reshape(-1)
else:
raise InputError("co_b should be a vector")
if co_b.size != n_row_co_a:
msg ='The number of row in co_a should be equal to'
msg+='the number of size in co_b'
raise InputError(msg)
else: # co_a is None:
msg = "ERROR: co_b is defined but co_a is not."
raise InputError(msg)

elif co_b.ndim > 2:
raise InputError("co_b should be a vector")
self.limitation = py2dmat.util.limitation.Inequality(co_a, co_b)

def prepare(self, proc_dir: Path):
self.logger.prepare(proc_dir)

def submit(
self, message: py2dmat.Message, nprocs: int = 1, nthreads: int = 1
) -> float:
x = self.mapping(message.x)
message_indeed = py2dmat.Message(x, message.step, message.set)
self.solver.prepare(message_indeed)
cwd = os.getcwd()
os.chdir(self.solver.work_dir)
self.solver.run(nprocs, nthreads)
os.chdir(cwd)
result = self.solver.get_results()
if self.limitation.judge(message.x):
x = self.mapping(message.x)
message_indeed = py2dmat.Message(x, message.step, message.set)
self.solver.prepare(message_indeed)
cwd = os.getcwd()
os.chdir(self.solver.work_dir)
self.solver.run(nprocs, nthreads)
os.chdir(cwd)
result = self.solver.get_results()
else:
result = np.inf
self.logger.count(message, result)
return result

Expand Down
32 changes: 27 additions & 5 deletions src/py2dmat/algorithm/_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import numpy as np

import py2dmat
import py2dmat.util.limitation
from py2dmat import exception, mpi

# for type hints
Expand Down Expand Up @@ -169,17 +170,38 @@ def _read_param(
if initial_list.ndim == 1:
initial_list = initial_list.reshape(1, -1)
if initial_list.size == 0:
initial_list = min_list + (max_list - min_list) * self.rng.rand(
num_walkers, self.dimension
)
# Repeat until an "initial_list" is generated
# that satisfies the constraint formula.
loop_count = 0
while True:
judge_result = []
initial_list = min_list + (max_list - min_list) * self.rng.rand(
num_walkers, self.dimension
)
for walker_index in range(num_walkers):
judge = self.runner.limitation.judge(
initial_list[walker_index,:])
judge_result.append(judge)
if all(judge_result):
break
else:
loop_count += 1
if initial_list.shape[0] != num_walkers:
raise exception.InputError(
f"ERROR: initial_list.shape[0] != num_walkers ({initial_list.shape[0]} != {num_walkers})"
)
if initial_list.shape[1] != self.dimension:
raise exception.InputError(
f"ERROR: initial_list.shape[1] != dimension ({initial_list.shape[1]} != {self.dimension})"
)
f"ERROR: initial_list.shape[1] != dimension ({initial_list.shape[1]} != {self.dimension})" )
judge_result = []
for walker_index in range(num_walkers):
judge = self.runner.limitation.judge(
initial_list[walker_index,:])
judge_result.append(judge)
if not all(judge_result):
raise exception.InputError(
"ERROR: initial_list does not satisfy the constraint formula."
)
return initial_list, min_list, max_list, unit_list

def _meshgrid(
Expand Down
52 changes: 52 additions & 0 deletions src/py2dmat/util/limitation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from abc import ABCMeta, abstractmethod

import numpy as np

import py2dmat

# for type hints
from pathlib import Path
from typing import List, Optional, TYPE_CHECKING, Dict, Tuple

class LimitationBase(metaclass=ABCMeta):
@abstractmethod
def __init__ (self, a: Optional[np.ndarray] = None,
b: Optional[np.ndarray] = None):
if (a is not None) or (b is not None):
if a is None:
msg = "ERROR: b is defined but a is not."
raise RuntimeError(msg)
if b is None:
msg = "ERROR: a is defined but b is not."
raise RuntimeError(msg)
if (a is None) and (b is None):
self.islimitary = False
else:
self.islimitary = True
self.a = a
self.b = b
self.n_formura = a.shape[0]
self.ndim = a.shape[1]

@abstractmethod
def judge(self, x: np.ndarray) -> bool:
pass

class Inequality(LimitationBase):
def __init__ (self, a: Optional[np.ndarray] = None,
b: Optional[np.ndarray] = None):
super().__init__(a, b)

def judge(self, x: np.ndarray) -> bool:
if self.islimitary :
judge_formula = []
for formula_index in range(self.n_formura):
value_formula = 0
for dim_index in range(self.ndim):
value_formula += self.a[formula_index, dim_index]*x[dim_index]
value_formula += self.b[formula_index]
judge_formula.append(value_formula>0)
judge_result = all(judge_formula)
else:
judge_result = True
return judge_result

0 comments on commit 168da7d

Please sign in to comment.