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 % Cooperative wall-clock budget checkpoint (options.timeout)
32 if lineTimeoutExceeded(options)
33 line_error(mfilename,'State space generation exceeded
the wall-clock time budget (options.timeout=%gs).', options.timeout);
35 state = State.fromMarginal(sn, ind, n, options);
36 space(end+1:end+size(state,1),(size(nmax,2)-size(state,2)+1):size(nmax,2)) = state;
37 n = pprodcon(n,lb,ub);
41 for bi=ub:-1:lb % reverse order so that largest vector determines size(space,2)
42 nset = multichoose(R,bi);
44 % Cooperative wall-clock budget checkpoint (options.timeout)
45 if nargin>=6 && lineTimeoutExceeded(options)
46 line_error(mfilename,'State space generation exceeded
the wall-clock time budget (options.timeout=%gs).', options.timeout);
48 state = State.fromMarginal(sn, ind, nset(j,:));
50 space(end+1:end+size(state,1),:) = state;
52 space(end+1:end+size(state,1),(end-size(state,2)+1):end) = state;
58space = unique(space,'rows');
59% now we remove states that are not reachable
62 nvarsSum = sum(sn.nvars(ind,:));
64 % toMarginal expects state_i to include trailing nvars columns and
65 % strips them off; fromMarginalBounds builds buffer-only rows, so
66 % pad with zeros to match
the expected layout.
67 if ~sn.isstation(ind) && nvarsSum > 0
68 state_full = [space(s,:), zeros(1, nvarsSum)];
70 state_full = space(s,:);
72 [ni,nir] = State.toMarginal(sn,ind,state_full);
74 if all(nir <= sn.classcap(ist,:)) && ni <= cap
83 space = space(keep,:);
85space = space(end:-1:1,:); % so that states with jobs in phase 1 comes earlier