1function space = fromMarginalBounds(sn, ind, lb, ub, cap, options)
2% SPACE = FROMMARGINALBOUNDS(QN, IND, LB, UB, CAP, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
7if nargin<6 %~exist(
'options',
'var')
8 options = Solver.defaultOptions;
12ist = sn.nodeToStation(ind);
13%isf = sn.nodeToStateful(ind);
15% returns all states lb<= x<= ub, where ub/lb are either a scalar (total
16% number of jobs) or a vector (per-class number of jobs)
18if isempty(lb), lb=0*ub; end
20if length(lb) == 1, isVectorLB =0; else, isVectorLB = 1; end
21if length(ub) == 1, isVectorUB =0; else, isVectorUB = 1; end
22if isVectorLB~=isVectorUB, line_error(mfilename,'Bounds must either be both vectors or both scalars'); end
24if isVectorUB && isVectorLB
25 nmax = State.fromMarginal(sn, ind, ub, options);
27 nmax = State.fromMarginal(sn, ind, ub, options);
31 state = State.fromMarginal(sn, ind, n, options);
32 space(end+1:end+size(state,1),(size(nmax,2)-size(state,2)+1):size(nmax,2)) = state;
33 n = pprodcon(n,lb,ub);
37 for bi=ub:-1:lb % reverse order so that largest vector determines size(space,2)
38 nset = multichoose(R,bi);
40 state = State.fromMarginal(sn, ind, nset(j,:));
42 space(end+1:end+size(state,1),:) = state;
44 space(end+1:end+size(state,1),(end-size(state,2)+1):end) = state;
50space = unique(space,'rows');
51% now we remove states that are not reachable
55 [ni,nir] = State.toMarginal(sn,ind,space(s,:));
57 if all(nir <= sn.classcap(ist,:)) && ni <= cap
66 space = space(keep,:);
68space = space(end:-1:1,:); % so that states with jobs in phase 1 comes earlier