Source code for surrogate.sampling.samFullFact

# Copyright 2016 Quan Pan
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#    http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Quan Pan <quanpan302@hotmail.com>
# License: Apache License, Version 2.0
# Create: 2016-12-02

import re

import numpy as np

[docs]def samFullFact(levels): """Create a general full-factorial design :param levels: An array of integers that indicate the number of levels of each input design factor. :returns: The design matrix with coded levels 0 to k-1 for a k-level factor This code was originally published by the following individuals for use with Scilab: - Copyright (C) 2012 - 2013 - Michael Baudin - Copyright (C) 2012 - Maria Christopoulou - Copyright (C) 2010 - 2011 - INRIA - Michael Baudin - Copyright (C) 2009 - Yann Collette - Copyright (C) 2009 - CEA - Jean-Marc Martinez website: forge.scilab.org/index.php/p/scidoe/sourcetree/master/macros Much thanks goes to these individuals. It has been converted to Python by Abraham Lee. :Example: >>> samFullFact([2, 4, 3]) array([[ 0., 0., 0.], [ 1., 0., 0.], [ 0., 1., 0.], [ 1., 1., 0.], [ 0., 2., 0.], [ 1., 2., 0.], [ 0., 3., 0.], [ 1., 3., 0.], [ 0., 0., 1.], [ 1., 0., 1.], [ 0., 1., 1.], [ 1., 1., 1.], [ 0., 2., 1.], [ 1., 2., 1.], [ 0., 3., 1.], [ 1., 3., 1.], [ 0., 0., 2.], [ 1., 0., 2.], [ 0., 1., 2.], [ 1., 1., 2.], [ 0., 2., 2.], [ 1., 2., 2.], [ 0., 3., 2.], [ 1., 3., 2.]]) """ n = len(levels) # number of factors nb_lines = np.prod(levels) # number of trial conditions H = np.zeros((nb_lines, n)) level_repeat = 1 range_repeat = np.prod(levels) for i in range(n): range_repeat /= levels[i] lvl = [] for j in range(levels[i]): lvl += [j] * level_repeat rng = lvl * range_repeat level_repeat *= levels[i] H[:, i] = rng return H
################################################################################ def samFF2n(n): """ Create a 2-Level full-factorial design Parameters ---------- n : int The number of factors in the design. Returns ------- mat : 2d-array The design matrix with coded levels -1 and 1 Example ------- :: >>> samFF2n(3) array([[-1., -1., -1.], [ 1., -1., -1.], [-1., 1., -1.], [ 1., 1., -1.], [-1., -1., 1.], [ 1., -1., 1.], [-1., 1., 1.], [ 1., 1., 1.]]) """ return 2 * samFullFact([2] * n) - 1 ################################################################################ def samFracFact(gen): """ Create a 2-level fractional-factorial design with a generator string. Parameters ---------- gen : str A string, consisting of lowercase, uppercase letters or operators "-" and "+", indicating the factors of the experiment Returns ------- H : 2d-array A m-by-n matrix, the fractional factorial design. m is 2^k, where k is the number of letters in ``gen``, and n is the total number of entries in ``gen``. Notes ----- In ``gen`` we define the main factors of the experiment and the factors whose levels are the products of the main factors. For example, if gen = "a b ab" then "a" and "b" are the main factors, while the 3rd factor is the product of the first two. If we input uppercase letters in ``gen``, we get the same result. We can also use the operators "+" and "-" in ``gen``. For example, if gen = "a b -ab" then the 3rd factor is the opposite of the product of "a" and "b". The output matrix includes the two level full factorial design, built by the main factors of ``gen``, and the products of the main factors. The columns of ``H`` follow the sequence of ``gen``. For example, if gen = "a b ab c" then columns H[:, 0], H[:, 1], and H[:, 3] include the two level full factorial design and H[:, 2] includes the products of the main factors. Examples -------- :: >>> samFracFact("a b ab") array([[-1., -1., 1.], [ 1., -1., -1.], [-1., 1., -1.], [ 1., 1., 1.]]) >>> samFracFact("A B AB") array([[-1., -1., 1.], [ 1., -1., -1.], [-1., 1., -1.], [ 1., 1., 1.]]) >>> samFracFact("a b -ab c +abc") array([[-1., -1., -1., -1., -1.], [ 1., -1., 1., -1., 1.], [-1., 1., 1., -1., 1.], [ 1., 1., -1., -1., -1.], [-1., -1., -1., 1., 1.], [ 1., -1., 1., 1., -1.], [-1., 1., 1., 1., -1.], [ 1., 1., -1., 1., 1.]]) """ # Recognize letters and combinations A = [item for item in re.split('\-?\s?\+?', gen) if item] # remove empty strings C = [len(item) for item in A] # Indices of single letters (main factors) I = [i for i, item in enumerate(C) if item == 1] # Indices of letter combinations (we need them to fill out H2 properly). J = [i for i, item in enumerate(C) if item != 1] # Check if there are "-" or "+" operators in gen U = [item for item in gen.split(' ') if item] # remove empty strings # If R1 is either None or not, the result is not changed, since it is a # multiplication of 1. R1 = _grep(U, '+') R2 = _grep(U, '-') # Fill in design with two level factorial design H1 = samFF2n(len(I)) H = np.zeros((H1.shape[0], len(C))) H[:, I] = H1 # Recognize combinations and fill in the rest of matrix H2 with the proper # products for k in J: # For lowercase letters xx = np.array([ord(c) for c in A[k]]) - 97 # For uppercase letters if np.any(xx < 0): xx = np.array([ord(c) for c in A[k]]) - 65 H[:, k] = np.prod(H1[:, xx], axis=1) # Update design if gen includes "-" operator if R2: H[:, R2] *= -1 # Return the fractional factorial design return H def _grep(haystack, needle): try: haystack[0] except (TypeError, AttributeError): return [0] if needle in haystack else [] else: locs = [] for idx, item in enumerate(haystack): if needle in item: locs += [idx] return locs